Lecture 03: ggplot2 and adding those finishing
touches
0.1.0 An overview of Advanced Graphics and Data Visualization in
R
“Advanced Graphics and Data Visualization in R” is
brought to you by the Centre for the Analysis of Genome Evolution &
Function’s (CAGEF) bioinformatics training initiative. This CSB1021 was
developed to enhance the skills of students with basic backgrounds in R
by focusing on available philosophies, methods, and packages for
plotting scientific data. While the datasets and examples used in this
course will be centred on SARS-CoV-2 epidemiological and genomic data,
the lessons learned herein will be broadly applicable.
This lesson is the third in a 6-part series. The aim for the end of
this series is for students to recognize how to import, format, and
display data based on their intended message and audience. The format
and style of these visualizations will help to identify and convey the
key message(s) from their experimental data.
The structure of the class is a code-along style in
R markdown notebooks. At the start of each lecture, skeleton versions of
the lecture will be provided for use on the University of Toronto datatools
Hub so students can program along with the instructor.
0.2.0 Lecture objectives
Last week we did a deep dive on some of the more popular and broadly
applicable visualizations for conveying basic ideas about your data.
This week will focus on tidying up your visualizations and adding those
extra finishing touches that will help polish them off. Adding,
removing, altering graphs. Getting these little details correct help you
to avoid alterations with additional software outside of R.
At the end of this lecture you will have covered the following
topics
- Altering and reproducing themes.
- Setting and changing the content of titles, axes, text, and
legends.
- Annotating with text, and highlighting.
- Altering your plot with new geoms, as well as data/axis/text
manipulations.
- Arranging plots together in the same figure.
0.3.0 A legend for text format in R markdown
grey background - a package, function, code, command or
directory. Backticks are also use for in-line code.
italics - an important term or concept or an individual file or
folder
bold - heading or a term that is being defined
blue text - named or unnamed
hyperlink
... - Within each coding cell this will indicate an area
of code that students will need to complete for the code cell to run
correctly.
Blue box: A key concept that is being introduced
Yellow box: Risk or caution
Green boxes: Recommended reads and resources to
learn Python
Red boxes: A comprehension question which may or may
not involve a coding cell. You usually find these at the end of a
section.
0.4.0 Lecture and data files used in this course
0.4.1 Weekly Lecture and skeleton files
Each week, new lesson files will appear within your RStudio folders.
We are pulling from a GitHub repository using this Repository
git-pull link. Simply click on the link and it will take you to the
University of Toronto datatools
Hub. You will need to use your UTORid credentials to complete the
login process. From there you will find each week’s lecture files in the
directory /2024-03-Adv_Graphics_R/Lecture_XX. You will find
a partially coded skeleton.Rmd file as well as all of the
data files necessary to run the week’s lecture.
Alternatively, you can download the R-Markdown Notebook
(.Rmd) and data files from the RStudio server to your
personal computer if you would like to run independently of the Toronto
tools.
0.4.2 Live-coding HTML page
A live lecture version will be available at camok.github.io
that will update as the lecture progresses. Be sure to refresh to take a
look if you get lost!
0.4.3 Post-lecture PDFs
As mentioned above, at the end of each lecture there will be a
completed version of the lecture code released as a PDF file under the
Modules section of Quercus.
0.4.4 Data used in this lesson
Today’s datasets will focus on a number of datasets we’ve used in our
previous lectures.
0.4.4.1 Dataset 1: Lecture03.RData
This data file contains 4 objects:
covid_phu_long.df: COVID-19 daily cases values
across Ontario public health units seen in lecture 01.
covid_phu_window.df: sliding window data generated
from covid_phu_long.df based on a 14-day rolling
mean.
phu_by_total_cases_desc: a list of Ontario PHUs in
descending order by caseload
covid_demographics_total.df: age group demographics
in a long-format that we generated in lecture 02.
0.5.0 Packages used in this lesson
tidyverse which has a number of packages including
dplyr, tidyr, stringr,
forcats and ggplot2
viridis helps to create color-blind palettes for our
data visualizations
lubridate and zoo are helper packages used
for working with date formats in R
ggthemes, directlabels,
ggforce, ggbeeswarm, gghighlight,
and ggExtra will provide us new geoms and methods for
plotting or altering how our plots look.
ggpubr for arranging our plots.
# None of these packages are already available on r.datatools
# install.packages("ggthemes", dependencies = TRUE)
# install.packages("directlabels", dependencies = TRUE)
# install.packages("ggforce", dependencies = TRUE)
# install.packages("ggbeeswarm", dependencies = TRUE)
# install.packages("gghighlight", dependencies = TRUE)
# install.packages("ggExtra", dependencies = TRUE)
# install.packages("ggpubr", dependencies = TRUE)
# install.packages("ggtext", dependencies = TRUE)
# Packages to help tidy our data
library(tidyverse)
# Packages for the graphical analysis section
library(viridis)
# New visualisation packages
library(ggthemes)
library(directlabels)
library(ggforce)
library(ggbeeswarm)
library(gghighlight)
library(ggExtra)
library(ggpubr)
library(ggtext)
# packages used for working with/formating dates in R
library(lubridate)
library(zoo)
1.0.0 Present your data in its best format and form
Last week in lecture 2 we spent our time highlighting various types
of plots and their variants while discerning the proper circumstances of
their use. Now that we know which plots to use and when to use them, we
can focus on how to clean up your visualizations so each can be
presented as its “best self”.
Through both lectures and assignments we have already glimpsed at
some of the commands and layers we can use to improve upon our graphs
whether that is by choosing colour, titles, or legend information. Today
we’ll explore those options more deeply so you don’t have to spend days
trying to get your visualizations to look perfect. We’ll revisit some
old plots and build them up from basics and tweak them to produce
this:

By the time we finish today, we’ll know how to manipulate many of the
elements of a ggplot.
Let’s start with our PHU caseload data from lecture 1. We’ll load it
from a .RData file along with some other helpful
objects.
# Load some pregenerated data tables for class
# Load Lecture03.RData
load("data/Lecture03.RData")
ls()
[1] "covid_demo_long.df" "covid_demographics_total.df"
[3] "covid_phu_long.df" "covid_phu_window.df"
[5] "currMod" "destinationDir"
[7] "fname" "fout"
[9] "ggh" "gitCred"
[11] "githubDir" "lastMod"
[13] "lectureDir" "lectureName"
[15] "mainDir" "originDir"
[17] "phu_by_total_cases_desc" "renderOut"
[19] "repo" "repoLocal"
[21] "repoURL" "sample.min"
[23] "termDir" "termGit"
[25] "timeout"
covid_demo_long.df
covid_demographics_total.df
covid_phu_long.df
covid_phu_window.df
currMod
destinationDir
fname
fout
ggh
gitCred
githubDir
lastMod
lectureDir
lectureName
mainDir
originDir
phu_by_total_cases_desc
renderOut
repo
repoLocal
repoURL
sample.min
termDir
termGit
timeout
# Remind ourselves what covid_phu_window.df looks like
head(covid_phu_window.df)
covid_phu_window.df %>%
# Filter for the top 5 infected PHUs
filter(public_health_unit %in% phu_by_total_cases_desc[1:5],
start_date >= as.Date("2020-12-01")) %>%
# redirect the filtered result to ggplot
# 1. Data
ggplot(.) +
# 2. Aesthetics
aes(x = start_date, y = window_mean, # Set our x and y axes
colour = fct_reorder(public_health_unit, window_mean, .desc=TRUE)) + # Reorder our PHUs
# 4. Geoms
geom_line(linewidth=1)

From our above plot, we can immediately see that we have issues that
need remedying:
- The overall font size of the plot is small (and I have old
eyes).
- The legend title is quite large and based specifically on the
aes() assignment used.
- Our axes names need to be updated and we could use a title too.
We’ll fix this problem later!
1.1.0 Control the display of all non-data elements with
theme()
Although we haven’t directly discussed themes yet, we have seen it
appearing here and there in our individual plots. The influence of
themes sets and controls the presentation of titles, labels,
text, background, legends, etc. You don’t directly change the actual
information presented in these elements.
Calls to theme() generally take the form of
theme(element.component.sub-component = element_*(parameter = value))
Some basic elements include line, rect, text, title, and
aspect.ratio. Altering these elements in theme() will alter
all elements of their kind (ie all lines, rectangles, text etc.).
Alternatively specific element components can be altered more directly.
The following table lists most of the possible theme elements and
components. They can be as specific as axis.title.x.top.
More detailed descriptions can be found here.
| axis |
x and y axis elements |
title, text, ticks, line |
x, y, length |
top, bottom, left, right |
| legend |
all legend elements |
background, margin, spacing, key, text, title,
position, direction, justification, box |
x, y, size, height, width, align, just, spacing |
|
| panel |
background plotting area |
background, border, spacing, grid |
x, y, major, minor |
|
| plot |
entire plot |
background, title, subtitle, caption, tax, margin |
position |
|
| strip |
facet labels |
background, placement, text, switch |
x, y, text, pad |
grid, wrap |
You update or set your individual elements using the
element_*() functions. Within each element you can
typically control aesthetics like fill, colour/color, size, etc. Below
is a summary of the elements of concern and their parameters. Specific
elements_*() will correspond with the above
theme elements.
| element_line() |
formatting of lines |
|
\(\checkmark\) |
\(\checkmark\) |
\(\checkmark\) |
\(\checkmark\) |
\(\checkmark\) |
|
|
|
|
|
|
|
| element_text() |
formatting of text |
|
\(\checkmark\) |
\(\checkmark\) |
|
|
|
\(\checkmark\) |
\(\checkmark\) |
\(\checkmark\) |
\(\checkmark\) |
\(\checkmark\) |
\(\checkmark\) |
\(\checkmark\) |
| element_rect() |
borders and background |
\(\checkmark\) |
\(\checkmark\) |
\(\checkmark\) |
\(\checkmark\) |
|
|
|
|
|
|
|
|
|
| element_blank() |
draws nothing, and assigns no space |
|
|
|
|
|
|
|
|
|
|
|
|
|
inherit.blank is an additional parameter you can use in
these functions that is normally set to FALSE. When set to
TRUE, if a parental layer uses
element_blank(), it will cause this element to be blank as
well.
For example axis.title is the parent of
axis.title.x. By setting the
inherit.blank = TRUE parameter, you can override/nullify
aesthetics assignment layers as long as a parent layers has set those
elements to element.blank(). It’s a good way to remove
additional layer effects if needed!
1.1.1 Move your legend(s) using the legend.position
option
Let’s start with one of the most oft-intrusive components of our
visualizations. While necessary, the legends often default to the
right-hand side of our visualizations where they can take up extra
horizontal space without requiring much vertical space!
1.1.1.1 Moving your legend within the plot area
When we are looking to move our legends to different positions, there
are 2 areas to consider. The first is the plot area itself which
surrounds the data panel (where our data is
plotted). The legend.position parameter can take in two
types of values. The first is a set of characters: top,
bottom, left, and right which
relates to the plot area.
Let’s start with altering our legend position within the plot area.
It’s taking up quite a bit of space on the side. We’ll worry about the
label issues later. For now, let’s move the legend to the bottom of the
plot. At the same time, let’s increase our overall text size for the
plot.
# Build our plot and data from scratch
covid_phu_window.df %>%
# Filter for the top 4 infected PHUs
filter(public_health_unit %in% phu_by_total_cases_desc[1:4],
start_date >= as.Date("2020-12-01")) %>%
# redirect the filtered result to ggplot
# 1. Data
ggplot(.) +
# 2. Aesthetics
aes(x = start_date, y = window_mean, # Set our x and y axes
colour = fct_reorder(public_health_unit, window_mean, .desc=TRUE)) + # Reorder our PHUs
# Theme elements
theme(text = element_text(size=20), # set text size to 20
### 1.1.1 Move the legend to the bottom
legend.position = "bottom"
) +
# 4. Geoms
geom_line(linewidth=1)

1.1.1.2 Move a legend to within your data panel
Instead of moving the legend to the bottom of our plot area, let’s
use the empty space in the top left corner of the data panel instead by
accessing the coordinate system (0:1, 0:1) that represents the relative
positioning of elements within the panel. This system, follows a
c(x, y) setup that matches the data panel with (0,0)
representing the lower left corner.
Before we move the legend onto our panel, however, we also have to
remember where the legend itself is anchoring when we move it.
Are we asking to put the bottom-right corner of the legend into the
top-left corner of the plot? Or do we want to match the legend anchor so
that the top-left corners are aligned?
Use the legend.justification parameter to properly set
this property when moving your legend. It uses the same two-point
coordinate concept that we’ll use for legend.position.
# Build our plot and data from scratch
covid_phu_window.df %>%
# Filter for the top 4 infected PHUs
filter(public_health_unit %in% phu_by_total_cases_desc[1:4],
start_date >= as.Date("2020-12-01")) %>%
# redirect the filtered result to ggplot
# 1. Data
ggplot(.) +
# 2. Aesthetics
aes(x = start_date, y = window_mean, # Set our x and y axes
colour = fct_reorder(public_health_unit, window_mean, .desc=TRUE)) + # Reorder our PHUs
# Theme elements
theme(text = element_text(size=20), # set text size to 20
### 1.1.1.2 Move the legend around to within the panel space
legend.justification = c(0, 1), # Set the point on the legend you are moving
legend.position = c(0.02, 0.95), # Set the point you are moving to
legend.direction = "horizontal"
) +
# 4. Geoms
geom_line(linewidth=1)

1.1.2 Update the background panel and lines
There are a few more things we can do to the plot for now that
include updating the background panel to get rid of the grey colour and
maybe darkening our axis tick lines and axis lines themselves.
- We’ll use the
panel.background parameter which expects
an element_rect() to define it’s properties.
panel.grid.* gives us access to the background axes
lines using element_line()
- We’ll work with
axis.* elements to to update their
format a bit too.
- Let’s spice up the
plot a little bit by setting the
overall background colour.
# Build our plot and data from scratch
covid_phu_window.df %>%
# Filter for the top 5 infected PHUs
filter(public_health_unit %in% phu_by_total_cases_desc[1:4]) %>%
# redirect the filtered result to ggplot
# 1. Data
ggplot(.) +
# 2. Aesthetics
aes(x = start_date, y = window_mean, # Set our x and y axes
colour = fct_reorder(public_health_unit, window_mean, .desc=TRUE)) + # Reorder our PHUs
# Theme elements
theme(text = element_text(size=20), # set text size to 20
# Move the legend around to within the panel space
legend.justification = c(0,1),
legend.position = c(0.02,0.95),
legend.direction = "horizontal",
### 1.1.2 Update the panel colour and line colours
panel.background = element_rect("white"),
panel.grid.major = element_line("grey"),
### 1.1.2 Use a black line for the axes
axis.line = element_line(colour="black"),
axis.text = element_text(colour="black", face="bold"),
### 1.1.2 Update the plot background colour
plot.background = element_rect("lightblue")
) +
# 4. Geoms
geom_line(linewidth=1)

One vs. multiple theme() layers: You’ll notice from
our code above, that we only make a single
call to the theme() layer. Each line, however,
represents a different element of the theme that we are altering. In
general, while the order of these items does matter, if it makes sense
for you, you can add multiple layers for theme()
grouping them by the specific element types you want to work with like
axes, background, and titles.
1.2.0 Use premade themes from ggplot2
In our above example we made alterations to the theme that affected
background colour and axis lines. While some of you may lean on the more
artistic side you can also use premade themes from both the
ggplot2 package and additional packages like
ggthemes. Below you’ll find a list of the themes from
ggplot2.
| theme_gray() |
Grey background colour, white grid lines. |
| theme_bw() |
White background colour, grey grid lines. |
| theme_linedraw() |
White background colour, black lines of various
widths |
| theme_light() |
White background colour, grey lines of various
widths |
| theme_dark() |
Dark background colour, grey lines of various
widths |
| theme_minimal() |
No background annotations, grey lines |
| theme_classic() |
White background, x/y axis lines, no grid lines |
| theme_void() |
A completely empty themes, white background, no axis or
grid lines |
If you find a theme that you mostly like,
you can use that as a base to your graph
before making additional theme()
alterations. Let’s try a few of these out.
# Build our plot and save to an object
phu_window.plot <- covid_phu_window.df %>%
# Filter for the top 5 infected PHUs
filter(public_health_unit %in% phu_by_total_cases_desc[1:4]) %>%
# redirect the filtered result to ggplot
# 1. Data
ggplot(.) +
# 2. Aesthetics
aes(x = start_date, y = window_mean, # Set our x and y axes
colour = fct_reorder(public_health_unit, window_mean, .desc=TRUE)) + # Reorder our PHUs
# Theme elements
### 1.2.0 Start with a base theme
theme_minimal() +
theme(text = element_text(size=20), # set text size to 20
# Move the legend around to within the panel space
legend.justification = c(0,1),
legend.position = c(0.02,0.95),
legend.direction = "horizontal",
# Update the panel to drop the minor y-axis grid lines
panel.grid.minor = element_blank(),
# Use a black line for the axes
axis.line = element_line(colour="black"),
axis.text = element_text(colour="black", face="bold"),
) +
# 4. Geoms
geom_line(size=1)
# plot our object to standard output
phu_window.plot

# Try to add theme_dark() to our plot. What are the consequences?
phu_window.plot + theme_dark()

Layer order matters! It cannot be stressed enough
that layer order matters. We’ve mentioned it in previous sections as we
work through these figures but the above code is our clearest example.
Even though we had set the font formats, and legend positions, all of
that was erased with a single added theme_dark() layer.
This is because the most recent layer
overrides all of the aesthetics from previous ones. Sometimes this has
only a small effect depending on the inheritance structure or it can
essentially reset everything! Caveat emptor!
1.3.0 ggthemes mimics visual styles from multiple
sources
If you are feeling a little more daring with your choices, you can
turn to the ggthemes packages to mimic styles from a number
of publications such as the Economist, and Wall Street Journal. You can
look up a list of the various themes at https://github.com/jrnold/ggthemes.
Like the themes provided by ggplot, you can also make edits to these
themes within your scripts.
Two additional package options with different colour palettes and
shapes are ggthemr and ggsci.
# Build our plot
covid_phu_window.df %>%
# Filter for the top 5 infected PHUs
filter(public_health_unit %in% phu_by_total_cases_desc[1:4]) %>%
# redirect the filtered result to ggplot
# 1. Data
ggplot(.) +
# 2. Aesthetics
aes(x = start_date, y = window_mean, # Set our x and y axes
colour = fct_reorder(public_health_unit, window_mean, .desc=TRUE)) + # Reorder our PHUs
# Theme elements
### 1.3.0 Switch to the stata theme
theme_stata() +
theme(text = element_text(size=20), # set text size to 20
# Move the legend around to within the panel space
legend.justification = c(0,1),
legend.position = c(0.02,0.95),
legend.direction = "horizontal",
# Use a black line for the axes
axis.line = element_line(colour = "black"),
axis.text = element_text(colour = "black", face="bold"),
) +
# 4. Geoms
geom_line(size=1)

2.0.0 Text content can be updated through a number of layers
Now that we have played around with how to reposition legends, and
other elements of your plot, we can discuss how to change the
actual text content of your plot. Many times we want to relabel
axes or legends, even legend labels. There are a number of layers we can
work through but we’ll present some of the simplest ways to accomplish
this.
2.1.0 Label titles and axes individually or with the
labs() command
Up to this point, we’ve seen the use of different commands to alter
the labels and titles like:
xlab(): Update the x-axis label.
ylab(): Update the y-axis label.
ggtitle(): Update the plot title.
You can also access multiple options within a single call to the
labs() layer which accepts the following parameters:
...: a list of name-value pairs that map back to an
aesthetic (ie x = "X-axis" or
colour = "Population")
Use the NULL value to remove a title for a specific
label.
title, subtitle: the title with a
subtitle displayed below
caption: the text for the caption is displayed in
the bottom-right by default
tag: figure text tag/label usually for figure panels
in manuscripts
Let’s relabel our plot axis and titles to be more accurate. For now
we’ll drop the Stata theme and go with our own
alteration of theme_minimal(). We’ll also include a caption
in the bottom right to explain how we display the 14-day rolling mean.
You’ll also notice that the extremely long legend title will be quite
easily fixed!
Note: a quick way of adding space to your titles, is
to include the \n character which inserts a carriage
return.
# Build our plot
covid_phu_window.df %>%
# Filter for the top 5 infected PHUs
filter(public_health_unit %in% phu_by_total_cases_desc[1:4]) %>%
# redirect the filtered result to ggplot
# 1. Data
ggplot(.) +
# 2. Aesthetics
aes(x = start_date, y = window_mean, # Set our x and y axes
colour = fct_reorder(public_health_unit, window_mean, .desc=TRUE)) + # Reorder our PHUs
# Theme elements
# Start with a base theme
theme_minimal() +
theme(text = element_text(size=20), # set text size to 20
# Move the legend around to within the panel space
legend.justification = c(0,1),
legend.position = c(0.02,0.95),
legend.direction = "horizontal",
# Update the panel to drop the minor axis grid lines
panel.grid.minor = element_blank(),
# Use a black line for the axes
axis.line = element_line(colour = "black"),
axis.text = element_text(colour = "black", face="bold"),
) +
### 2.1.0 Add labels to our plot
labs(title = "Mean cases of COVID-19 in a 14-day window across top 4 Ontario Public Health Units\n",
x = "\nWindow date",
y = "Mean cases in 14-day window\n",
colour = "Public Health Unit",
caption = "*14-day rolling mean with date as start of the window") +
# 4. Geoms
geom_line(linewidth=1)

2.2.0 Relabel axis ticks, and legend labels with the
labels parameter
In last lecture’s assignment, you likely would have used the
xlim() or ylim() layers to set the axis limits
on some of your visualizations. As with all things, there is more than
one pathway to our goals.
The scale_*() functions can also be used to set the
title, limits, breaks, and labels along your axes. Some of these
parameters are redundant and can override other ggplot2
layer commands, depending on the order you have included them.
| name |
xlab(), ylab(), lab(x), lab(y) |
| limits |
xlim(), ylim() |
| break |
Determine when axis tick marks are generated |
| labels |
Rename the labels present at
axis tick marks |
2.2.1 Relabel date axes and legend labels with
scale_*_date()
We’ll start with a familiar idea we’ve been working with since
lecture 1. A good portion of our pandemic visualizations have focused on
looking at data over time. With the scale_x_date() layer,
we have set limits, breaks and label formats. Unlike more discrete data
sets that we’ll see later, the scale_*_date() layer has
some very specific parameters that surround the idea of dates and how
they are formatted. Last week we took a close look at
scale_x_date() in section 3.3.2 of the
lecture:
breaks: while you can set specific breaks for dates
with this parameter you will need a specific vector of date
values that matches your own data groups.
date_breaks: a convenient string representation to
describe the distance between breaks like
"12 days", or "3 years". This parameter will
override any information passed to breaks.
date_labels: a convenient string representation to
describe the format of dates defined by
strftime(). Information found here
Let’s start by relabeling our x-axis to show us our dates by month
and at the same time we set a limit to show us data starting in December
of 2020. We’ve done this before so it should be easy.
# Build our plot
covid_phu_window.df %>%
# Filter for the top 5 infected PHUs
filter(public_health_unit %in% phu_by_total_cases_desc[1:4]) %>%
# redirect the filtered result to ggplot
# 1. Data
ggplot(.) +
# 2. Aesthetics
aes(x = start_date, y = window_mean, # Set our x and y axes
colour = fct_reorder(public_health_unit, window_mean, .desc=TRUE)) + # Reorder our PHUs
# Theme elements
# Start with a base theme
theme_minimal() +
theme(text = element_text(size=20), # set text size to 20
# Move the legend around to within the panel space
legend.justification = c(0,1),
legend.position = c(0.02,0.95),
legend.direction = "horizontal",
# Update the panel to drop the minor axis grid lines
panel.grid.minor = element_blank(),
# Use a black line for the axes
axis.line = element_line(colour = "black"),
axis.text = element_text(colour = "black", face="bold"),
) +
# Add labels to our plot
labs(title = "Mean cases of COVID-19 in a 14-day window across top 4 Ontario Public Health Units\n",
x = "\nWindow date",
y = "Mean cases in 14-day window\n",
colour = "Public Health Unit",
caption = "*14-day rolling mean with date as start of the window") +
# 3. Scaling
### 2.2.1 Start looking at data from December 2020 onwards
scale_x_date(... = c(as.Date("2020-12-01"), # Set a start date for our limit
as.Date(max(covid_phu_window.df$start_date))), # Identify the last date and use that
... = "1 month", # How will we break up the dates?
... = "%b-%Y") + # How will we format labels
# 4. Geoms
geom_line(linewidth=1)
Error in scale_x_date(... = c(as.Date("2020-12-01"), as.Date(max(covid_phu_window.df$start_date))), : unused arguments (... = c(as.Date("2020-12-01"), as.Date(max(covid_phu_window.df$start_date))), ... = "1 month", ... = "%b-%Y")
2.2.1.1 Adjust your axis text with the element_text()
function
At this point you’ll notice that our x-axis text is also pretty
unclean. Let’s revisit the axis.text.x component of theme
to deal with this. There are a few things we can influence with this
element_text() including:
angle: use this to rotate text from a horizontal
position, in a counter-clock-wise direction.
vjust and hjust: the
vertical and horizontal justification
of your text as a value from 0 to 1, where 0.5 is “centered”.
family: determine the font used
face: determine the font face (plain, bold, italic,
bold.italic)
size, lineheight, color,
colour: alter other characteristics of your text
display
debug: a handy tool that draws a border around your
complete text area and a point where each label is anchored. Great for
helping to tweak parameters to get that “perfect” look on your figures
but not meant to remain in the final figure.
Let’s fix up our current visualization by rotating our text and
right-justifying it.
# Build our plot
covid_phu_window.df %>%
# Filter for the top 5 infected PHUs
filter(public_health_unit %in% phu_by_total_cases_desc[1:4]) %>%
# redirect the filtered result to ggplot
# 1. Data
ggplot(.) +
# 2. Aesthetics
aes(x = start_date, y = window_mean, # Set our x and y axes
colour = fct_reorder(public_health_unit, window_mean, .desc=TRUE)) + # Reorder our PHUs
# Theme elements
# Start with a base theme
theme_minimal() +
theme(text = element_text(size=20), # set text size to 20
# Move the legend around to within the panel space
legend.justification = c(0,1),
legend.position = c(0.02,0.95),
legend.direction = "horizontal",
# Update the panel to drop the minor axis grid lines
panel.grid.minor = element_blank(),
# Use a black line for the axes
axis.line = element_line(colour = "black"),
axis.text = element_text(colour = "black", face="bold"),
### 2.2.1.1 Adjust the x-axis text
axis.text.x = element_text(angle = ..., # Rotate 90
hjust = ..., # Right-justify
vjust = ...) # Centre text "vertically" on axis tick
) +
# Add labels to our plot
labs(title = "Mean cases of COVID-19 in a 14-day window across top 4 Ontario Public Health Units\n",
x = "\nWindow date",
y = "Mean cases in 14-day window\n",
colour = "Public Health Unit",
caption = "*14-day rolling mean with date as start of the window") +
# 3. Scaling
# Start looking at data from December 2020 onwards
scale_x_date(limits = c(as.Date("2020-12-01"), # Set a start date for our limit
as.Date(max(covid_phu_window.df$start_date))), # Identify the last date and use that
date_breaks = "1 month", # How will we break up the dates?
date_labels = "%b-%Y") + # How will we format labels
# 4. Geoms
geom_line(linewidth=1)
Error in mget(args, envir = env): '...' used in an incorrect context
2.2.2 Relabel continuous axis ticks by altering limits
and breaks
Much of your quantitative data will usually come as a continuous
series of values. We’ve played around with these scales before using
scale_*_log10 in lecture and assignment. Similarly, we can
alter continuous axes without necessarily transforming them. This is
accomplished via the scale_*_continuous() layer. With these
types of layers, we have access to parameters like:
breaks, minor_breaks: a numeric vector
of positions OR a function that takes the limits as input and returns
breaks as output for the parameter specified.
n.breaks: an integer to suggest the number of major
breaks. The plotting algorithm may alter this value to ensure nice break
labels. This will only work if breaks = waiver() (the
default for breaks).
labels: a character vector matching labels to the
major breaks.
limits: a numeric vector
c(lower, upper)
Let’s break our y-axis into major tick-marks of every 500 cases by
altering scale_y_continuous() with the seq()
function. At the same time, let’s remove the title from our legend by
setting the guide in labs() to a NULL
value.
# Build our plot and save to an object for later use
phu_window.plot <- covid_phu_window.df %>%
# Reorder the PHU factor here
mutate(public_health_unit = fct_reorder(public_health_unit, window_mean, .desc=TRUE)) %>%
# Filter for the top 5 infected PHUs
filter(public_health_unit %in% phu_by_total_cases_desc[1:4]) %>%
# redirect the filtered result to ggplot
# 1. Data
ggplot(.) +
# 2. Aesthetics
aes(x = start_date, y = window_mean, # Set our x and y axes
colour = public_health_unit) +
# Start with a base theme
theme_minimal() +
theme(text = element_text(size=20), # set text size to 20
# Move the legend around to within the panel space
legend.justification = c(0,1),
legend.position = c(0.02,0.95),
legend.direction = "horizontal",
# Update the panel to drop the minor axis grid lines
panel.grid.minor = element_blank(),
# Use a black line for the axes
axis.line = element_line(colour = "black"),
axis.text = element_text(colour = "black", face="bold"),
# Adjust the x-axis text
axis.text.x = element_text(angle = 90, # Rotate 90
hjust = 1, # Right-justify
vjust = 0.5) # Centre text "vertically" on axis tick
) +
# Add labels to our plot
labs(title = "Mean cases of COVID-19 in a 14-day window across top 4 Ontario Public Health Units\n",
x = "\nWindow date",
y = "Mean cases in 14-day window\n",
colour = NULL,
caption = "*14-day rolling mean with date as start of the window") +
# 3. Scaling
# Start looking at data from July 2020 onwards
scale_x_date(limits = c(as.Date("2020-12-01"), # Set a start date for our limit
as.Date(max(covid_phu_window.df$start_date))), # Identify the last date and use that
date_breaks = "1 month", # How will we break up the dates?
date_labels = "%b-%Y") + # How will we format labels
### 2.2.2 Change our y-axis breaks
... +
# 4. Geoms
geom_line(linewidth=1)
Error in eval(expr, envir, enclos): '...' used in an incorrect context
# plot our object to standard output
phu_window.plot

2.2.3 Relabel discrete axes and legend labels in
scale_*_discrete()
For various reasons, you may have categorical or grouped data with
unusual names. It may be convenient to code your data this way but
letting ggplot2 assign these to your axes or labels may not
be suitable. Instead, you can manually rename them using the
labels parameter with your various
scale_*_discrete() layers.
When manually labeling your categories be sure to supply a vector
with the correct number of arguments to match the number of levels in
your categories or groups.
Let’s revisit some of our age-grouped data from last week which was
visualized as grouped violin plot with inset
boxplots. Recall that our data was labelled by the
variable age_group using “0 to 4”, “5 to 11”, etc. We’ll
modify those in the plot (rather than the data frame) to a format that
looks like “0-4”, “5-11”, etc.
# Let's briefly review the dataset
str(covid_demographics_total.df, give.attr = FALSE)
gropd_df [476 x 15] (S3: grouped_df/tbl_df/tbl/data.frame)
$ period : chr [1:476] "recent" "recent" "recent" "recent" ...
$ from_date : chr [1:476] "18-Feb-23" "18-Feb-23" "18-Feb-23" "18-Feb-23" ...
$ to_date : chr [1:476] "04-Mar-23" "04-Mar-23" "04-Mar-23" "04-Mar-23" ...
$ public_health_unit : chr [1:476] "Algoma" "Algoma" "Algoma" "Algoma" ...
$ age_group : Factor w/ 7 levels "0 to 4","5 to 11",..: 1 3 4 5 2 6 7 1 3 4 ...
$ total_cases : num [1:476] 4 2 15 24 1 19 8 2 1 4 ...
$ total_rate : num [1:476] 83.6 22.2 57.6 84.4 12.5 ...
$ total_hospitalizations_count: num [1:476] 0 0 0 2 0 8 2 0 0 0 ...
$ total_hospitalizations_rate : num [1:476] 0 0 0 7 0 24.3 25.1 0 0 0 ...
$ male_cases : num [1:476] 2 0 4 9 0 11 4 1 0 0 ...
$ female_cases : num [1:476] 2 2 11 15 1 8 4 1 1 4 ...
$ percent_cases : num [1:476] 0.0548 0.0274 0.2055 0.3288 0.0137 ...
$ percent_hospitalizations : num [1:476] 0 0 0 0.167 0 ...
$ percent_male_cases : num [1:476] 0.5 0 0.267 0.375 0 ...
$ percent_female_cases : num [1:476] 0.5 1 0.733 0.625 1 ...
# Build and save the plot for later use
demographics.plot <- covid_demographics_total.df %>%
# Ungroup this dataframe to clean it up a little
ungroup() %>%
# Filter for cumulative data
filter(period == "cumulative") %>%
# Select for just the important columns
select(public_health_unit, age_group, percent_cases, percent_hospitalizations) %>%
# Pivot the modified table to capture the "stat_group" of percent_cases vs percent_hospitalizations
pivot_longer(cols=c(3,4), names_to = "stat_group", values_to = "percent_PHU_total") %>%
# Plot the data as a grouped violin plot with inset boxplot
# 1. Data
ggplot(.) +
# 2. Aesthetics
aes(x=age_group, y = percent_PHU_total) +
# Start with a base theme
theme_minimal() +
theme(text = element_text(size=20), # set text size to 20
# Move the legend around to within the panel space
legend.justification = c(0,1),
legend.position = c(0.02,0.95),
legend.direction = "horizontal",
# Update the panel to drop the minor axis grid lines
panel.grid.minor = element_blank(),
# Use a black line for the axes
axis.line = element_line(colour = "black"),
axis.text = element_text(colour = "black", face="bold"),
) +
# Add labels to the plot
labs(title = "Percent cases and hospitalizations by proportion per PHU across age group - cumulative across pandemic",
x = "\nAge group",
y = "Proportion of reported PHU data\n",
caption = "\n*Age group values are calculated as a percentage of total cases or hospitalizations within a PHU") +
# 3. Scaling
scale_y_continuous(limits = c(0, 0.5)) + # Set the limits of our y-axis
scale_colour_manual(values=c("black", "black"))+ # we'll need this to fix our boxplot outlines
### 2.2.3 Set the labels of our x-axis categories
scale_x_discrete(...=c("0-4", "5-11", "12-19", "20-39", "40-59", "60-79", "80+"))+
# 4. Data
# multi-factor violin plots but keep the width consistent
geom_violin(scale="width", aes(fill=stat_group)) +
# Boxplot but smaller width so they reside "within" the violin plot
geom_boxplot(aes(colour = stat_group), width=0.2,
position = position_dodge(width=0.9),
outlier.shape=NA) + # Remove the outliers
# Add in all of the data points
geom_quasirandom(dodge.width = 0.85, aes(group=stat_group), alpha = 0.8)
Error in discrete_scale(aesthetics = c("x", "xmin", "xmax", "xend"), name = name, : unused argument (... = c("0-4", "5-11", "12-19", "20-39", "40-59", "60-79", "80+"))
# Show the plot
demographics.plot
Error in eval(expr, envir, enclos): object 'demographics.plot' not found
2.3.0 Alter your legends with the guide parameter or
guides() layer
Nearly there with updating this plot! We’ve relabeled the the x-axis
categories but our legend title isn’t quite there. Previously we used
the labs() layer to handle this aspect but this time around
we really want to also alter the labels of our data categories to “%
cases” and “% hospitalizations”. Before we get into that, let’s talk a
little more about legends.
Normally you can let ggplot2 take the wheel and
automatically generate guides for you. Whenever you set
colour/fill/linetype etc in your aesthetics, this will generate a
legend. When the groups are mapped in the same way (i.e. the same
labels!) between different aesthetics, the legends may be combined.
There will be instances, however, when you need to adjust your legend
or get rid of it all together. This could range from titles, to
combining your guides across different aesthetics commands. There are a
number of ways to achieve the same result when working with guides and
we’ll go through a number of examples. First, however, we should discuss
the types of legends:
| guide_legend() |
legend |
The base prototype of the legend which integrates how
geoms are mapped into values. |
| guide_bins() |
bins |
A binned version of legends which places ticks between
keys and has its own small axis |
| guide_colourbar() |
colourbar |
For mapping continous colour/fill scales from using
scale_fill_*() and scale_colour_*(). |
| guide_coloursteps() |
coloursteps |
A version of guide_colourbar() except for binned colour
and fill scales rather than gradients. |
| none |
NA |
Suppress the legend as specified |
We briefly saw the use of a colourbar in our last lecture when using
a continuous variable to set the colour of our barplots. Each type has
it’s own use depending on how you want to describe your data. Within
each of the guide types, you can update parameters about text within the
legend.
| title |
name, position, theme, hjust, vjust |
| label |
name, position, theme, hjust, vjust |
| key |
width, height |
| order |
you can determine the order of the guide amongst others
using integers [1:99]. 0 sets order by an algorithm |
| other |
direction of guide, number of rows/cols |
So where can you use these methods?
2.3.1 Use scale_*() to set guide parameters
Within each scale_*() you declare you can set the
parameter guide to one of the above guide types. To exclude
a legend for that particular type, set the value to
none.
Some layer options you may work with here are
scale_fill_discrete() , scale_shape_manual()
and scale_colour_continuous()
Let’s update our fill guide to change the legend title to “Data
category” and relabel our categories to “% cases” and “%
hospitalizations” as previously discussed.
# Adjust the fill scale layer for the demographics plot
demographics.plot +
### 2.3.1 Set the fill guide details
...(name = "Data category", # Guide name
labels = c("% cases", "% hospitalizations")) # Relabel the categories
Error in eval(expr, envir, enclos): object 'demographics.plot' not found
2.3.2 Use the guides() layer to manipulate multiple
guides
While our output is nearly correct, there is still a problem! Now
have two sets of legends! If you look carefully at the ggplot code,
you’ll see that we set aesthetics in three places:
geom_violin(scale="width", aes(fill=stat_group))
geom_boxplot(aes(colour = stat_group)...
geom_quasirandom(dodge.width = 0.85, aes(group=stat_group), alpha = 0.8)
Across 3 geoms we’ve generated 3 aesthetic groups: fill,
colour and group. Remember when we said that
ggplot would take the wheel and generate legend/guide
information automatically? Well this is a case where all three are
mapping by the same variable so they get combined into a single legend.
When we took the time to change the fill guide, however, it
was broken away from the other two (geom_boxplot and
geom_quasirandom) guides.
In a case like this we use the guides() layer to set
multiple guides at once using the scale types as parameters ie colour,
size, shape. Much like labs() it gives us centralized
access to guide format and settings, allowing us to quickly rectify our
problem. In this case, we really don’t need the group or
colour aesthetics, so we’ll simply get rid of them.
# Adjust the fill scale layer for the demographics plot
demographics.plot +
### 2.3.2 Use the guides() layer and get rid of the scale_fill_discrete() layer
guides(fill = guide_legend(title = "Data category"),
colour = ..., group = ...) +
# Set the fill guide details
scale_fill_discrete(labels = c("% cases", "% hospitalizations")) # Relabel the categories
Error in eval(expr, envir, enclos): object 'demographics.plot' not found
Try to minimize your layers: In our above example we
had to use scale_fill_discrete() and
guides() because we needed to manipulate multiple
guides but only a couple in a very simple way. This format, however,
might not always be the best choice. For instance, suppose you wanted to
explicitly choose your violin colours? Then a
scale_fill_manual() layer would be required, at which
point you need to decide, will you set your guide format all in this
layer or work with a separate guides() layer? Depending
on the complexity of your guides (as we’ll revisit later) it may be
easier to keep them centralized. In other cases, you may want to set
them within their own scale_*() layers in case you want
to make changes to specific layer details more centralized. It’s a
balance that will be struck between your specific needs but try to be
thoughtful about it to save yourself some pain in editing your code
later on.

Well you could rely on the basic colour palette but you’re better off
picking your own colours!
3.0.0 Colour palettes!
Up to this point, we’ve danced around the idea of colour in our
lectures and assignments. For those of you that aren’t familiar with
your colour choices, here is a quick breakdown of colour palettes.
A common thing to want to do is to change colours from
ggplot2’s default rainbow palette. There are many reasons
to change a colour palette including
- making it easier on the reader’s eye.
- making it colour-blind friendly.
- ensuring that plots with continuous data use good colour spectra for
accurate representation.
When we talk about colour palettes and their purpose, there are 3
main types.
3.0.1 Use sequential colour palettes to display low to high
values
Sequential - implies an order to your data - i.e. light to
dark implies low values to high values. There are helpful when working
with continuous data scales of increasing value e.g. heatmaps.
# Load the RColorBrewer library
library(RColorBrewer)
# display the sequential colour palettes
display.brewer.all(type = "seq")

3.0.2 Use diverging colour palettes to highlight the middle and
extremes of a distribution
Diverging - low and high values are extremes, and the middle
values are important. This palette will goes from light to dark, middle
to outsides with 3 colours mainly used.
# Display the diverging colour palettes
display.brewer.all(type = "div")

3.0.3 Use qualitative colour palettes for categorical data
Qualitative - there is no quantitative relationship between
colours. This is usually used for categorical data when you want each
category to be visualized distinctly.
display.brewer.all(type = "qual")

3.1.0 Add a colour palette to a plot like a layer
Let’s test one of the RColorBrewer palettes out on our
data. We’ll add it as a layer to phu_window.plot using
scale_colour_brewer() to override the colour mappings
defined in the aes() layer of the plot. Some parameters we
can keep in mind:
type: determines the kind of palette as sequential
(seq), diverging (div) or qualitative (qual)
palette: accepts a string name for a palette or an
integer that combines with type to pick a palette
Note that colour palettes are not vector
recycled when plotting in ggplot. This means if you don’t
supply enough colours to match your groups, then unassigned groups will
simply be cut off or not displayed.
More information on palette order and other parameters can be found
here
phu_window.plot +
# Use the Dark2 palette
scale_colour_brewer(palette=...)
Error in eval(expr, envir, enclos): '...' used in an incorrect context
phu_window.plot +
# Pick a qualitative colour palette
scale_colour_brewer(type=..., palette=...)
Error in eval(expr, envir, enclos): '...' used in an incorrect context
3.2.0 You can always pick your own colours!
You can always choose a vector of your own colors using this ‘R color
cheatsheet’ (https://www.nceas.ucsb.edu/~frazier/RSpatialGuides/colorPaletteCheatsheet.pdf).
Names of colours as well as hex colour codes are accepted. You can
supply a manual list using the scale_*_manual()
command.
phu_window.plot +
# Set your own manual colour choices
scale_colour_manual(values=c(..., "cornflowerblue", "orange", ...))
Error in is_missing(values): '...' used in an incorrect context
3.3.0 Colour-blind friendly palettes can be found in the
viridis package
The viridis package also has some nice color palettes
(https://cran.r-project.org/web/packages/viridis/vignettes/intro-to-viridis.html).
These colour packages are diverging palettes meant to help highlight
true colour change across continuous scales. You’ve seen it come up a
few times in our data and these palettes do well for small categorical
sets but begin to blend as our number of categories increase in
size.
The main calls we can use follow the format
scale_*_viridis_c/d/b() where the “c/d/b” represents
continuous/discrete/binned data and the types of additional arguments
that can be passed on to augment the call. There are some additional
parameters that can be used to set the colours when called:
option: accepts one of 8 possible character
representing 5 colour scales; “magma”/“A”, “inferno”/“B”, “plasma”/“C”,
“viridis”/“D” or “cividis”/“E”.
direction: sets the direction of the palette order.
Use -1 to reverse it.
phu_window.plot +
# Use a colour-blind friendly palette
scale_colour_viridis_d(...)
Error in eval(expr, envir, enclos): '...' used in an incorrect context
3.4.0 Use after_scale to set an aesthetic mapping
dependent upon another one
There may be times when you want to link certain aesthetics to each
other like colour and fill for instance.
Perhaps you want to set both to a custom value but one as a lighter
shade. Rather than set both mappings to a data variable and
then using a scale layer to set the values, you can set one mapping
as dependent upon another. There are transformations and mappings of
data to aesthetics happening under the hood at 3 stages when evaluating
a ggplot object.
- The default stage takes place when you first use the aes() layer to
map the data layer directly to an aesthetic.
- The second stage occurs after data has been transformed by the
stat() layer. This occurs in places where data is summarized, such as a
geom_bar() as the data isn’t directly being used but rather summarized
into a set of data for display. You would use
after_stat()
to access this data.
- The final stage is after the data has been transformed and mapped by
the plot scales (eg
scale_colour_manual()). From there, you
can dictate how another aesthetic mapping will determine its
values.
Using the after_scale() function will postpone an
aesthetic mapping until after the data has been scaled. As we’ll see
next, when used properly, you will tie the aesthetics of one aspect to
the aesthetics of another. There are a number of cool ways you can
utilize after_stat() as well to add
finishing touches like counts/values to your graphs. The
after_scale() feature will also simplify our code so that
if we want to change one aspect, then all dependent aspects will change
with it.
Going back to our previous boxplot, we’ll utilize
after_scale() to link the fill values of our violin plot
to the colour set of the same violin plot. At
the same time we’ll de-couple those aesthetics from the ones we use in
the inset boxplot of the visualizations. Enough talk though, let’s see
what that looks like.
# Build and save the plot for later use
demographics.plot <- covid_demographics_total.df %>%
# Ungroup this dataframe to clean it up a little
ungroup() %>%
# Filter for cumulative data
filter(period == "cumulative") %>%
# Select for just the important columns
select(public_health_unit, age_group, percent_cases, percent_hospitalizations) %>%
# Pivot the modified table to capture the "stat_group" of percent_cases vs percent_hospitalizations
pivot_longer(cols=c(3,4), names_to = "stat_group", values_to = "percent_PHU_total") %>%
# Plot the data as a grouped violin plot with inset boxplot
# 1. Data
ggplot(.) +
# 2. Aesthetics
aes(x=age_group, y = percent_PHU_total) +
# Start with a base theme
theme_minimal() +
theme(text = element_text(size=20), # set text size to 20
# Move the legend around to within the panel space
legend.justification = c(0,1),
legend.position = c(0.02,0.95),
legend.direction = "horizontal",
# Update the panel to drop the minor axis grid lines
panel.grid.minor = element_blank(),
# Use a black line for the axes
axis.line = element_line(colour = "black"),
axis.text = element_text(colour = "black", face="bold"),
) +
# Add labels to the plot
labs(title = "Percent cases and hospitalizations by proportion per PHU across age group",
x = "\nAge group",
y = "Proportion of reported PHU data\n",
caption = "\n*Age group values are calculated as a percentage of total cases or hospitalizations within a PHU") +
### Use the guides() layer and get rid of the scale_fill_discrete() layer
guides(fill = "none", group = "none") +
# 3. Scaling
scale_y_continuous(limits = c(0, 0.5)) + # Set the limits of our y-axis
# Set the labels of our x-axis categories
scale_x_discrete(labels=c("0-4", "5-11", "12-19", "20-39", "40-59", "60-79", "80+")) +
# Set the colour legend
scale_colour_discrete(name = "Data category", labels = c("% cases", "% hospitalizations")) +
# 4. Data
# multi-factor violin plots but keep the width consistent
### 3.4.0 Link your fill to the colour aesthetic
geom_violin(scale="width",
aes(colour = stat_group, fill=...),
lwd = 1.5) +
# Boxplot but smaller width so they reside "within" the violin plot
geom_boxplot(aes(fill = stat_group), width=0.2,
position = position_dodge(width=0.9),
outlier.shape=NA) + # Remove the outliers
# Add in all of the data points
geom_quasirandom(dodge.width = 0.85, aes(group=stat_group), alpha = 0.8)
Error in layer(data = data, mapping = mapping, stat = stat, geom = GeomViolin, : '...' used in an incorrect context
# Show the plot
demographics.plot
Error in eval(expr, envir, enclos): object 'demographics.plot' not found
Section 3.0.0 Comprehension Question Are you
convinced of the benefits or differences in using
after_scale()? Play with the code below and see what
happens when you use scale_fill_manual() or
scale_colour_manual() to set different values for your
fill vs colour aesthetics? What is the difference between setting these
two layers in the context of using after_scale()?
covid_demographics_total.df %>%
# Ungroup this dataframe to clean it up a little
ungroup() %>%
# Filter for cumulative data
filter(period == "cumulative") %>%
# Select for just the important columns
select(public_health_unit, age_group, percent_cases, percent_hospitalizations) %>%
# Pivot the modified table to capture the "stat_group" of percent_cases vs percent_hospitalizations
pivot_longer(cols=c(3,4), names_to = "stat_group", values_to = "percent_PHU_total") %>%
# Plot the data as a grouped violin plot with inset boxplot
# 1. Data
ggplot(.) +
# 2. Aesthetics
aes(x=age_group, y = percent_PHU_total) +
# Start with a base theme
theme_minimal() +
theme(text = element_text(size=20), # set text size to 20
# Move the legend around to within the panel space
legend.justification = c(0,1),
legend.position = c(0.02,0.95),
legend.direction = "horizontal",
# Update the panel to drop the minor axis grid lines
panel.grid.minor = element_blank(),
# Use a black line for the axes
axis.line = element_line(colour = "black"),
axis.text = element_text(colour = "black", face="bold"),
) +
# Add labels to the plot
labs(title = "Percent cases and deaths by proportion per PHU across age group",
x = "\nAge group",
y = "Proportion of reported PHU data\n",
caption = "\n*Age group values are calculated as a percentage of total cases or deaths within a PHU") +
### Use the guides() layer and get rid of the scale_fill_discrete() layer
guides(fill = "none", group = "none") +
# 3. Scaling
scale_y_continuous(limits = c(0, 0.5)) + # Set the limits of our y-axis
# Set the labels of our x-axis categories
scale_x_discrete(labels=c("0-4", "5-11", "12-19", "20-39", "40-59", "60-79", "80+")) +
# Set the colour legend
### 3.0.0 Comprehension question
scale_colour_manual(name = "Data category", labels = c("% cases", "% hospitalizations"),
values = c(...)) +
scale_fill_manual(name = "Data category", labels = c("% cases", "% hospitalizations"),
values = c(...)) +
# 4. Data
# multi-factor violin plots but keep the width consistent
### Link your fill to the colour aesthetic
geom_violin(scale="width",
aes(colour = stat_group, fill=after_scale(alpha(colour, 0.3))),
lwd = 1.5) +
# Boxplot but smaller width so they reside "within" the violin plot
geom_boxplot(aes(fill = stat_group), width=0.2,
position = position_dodge(width=0.9),
outlier.shape=NA) + # Remove the outliers
# Add in all of the data points
geom_quasirandom(dodge.width = 0.85, aes(group=stat_group), alpha = 0.8)
Error in is_missing(values): '...' used in an incorrect context

It’s all about figuring out how to add those finishing touches
4.0.0 Annotating your plots
After preparing your visualization you may consider adding extra
annotations. These are usually layers that don’t affect the
aesthetics or data of your visualization but depending on how you add
them and the package you are using this isn’t strictly true. For the
most part, however, let’s consider your annotations as separate from
your plot.
We’ve already dabbled in annotations since the first lecture but now
we’re going to look deeply at how these work and some more advanced
annotation packages.
4.1.0 annotate() plots with shapes, text, and
arrows.
Sometimes you need to add some additional text, or shapes to your
graph that aren’t necessarily a part of the data itself. In other words
you would like to annotate your plot. To accomplish this you
can use the annotate() function which will essentially add
geoms to your plot. While these annotations can affect the axis limits
of your plot if it is required to show your annotation(s), they won’t
affect the legends nor be treated as actual data - just an overlay to
your plot.
The annotate() geom has the following parameters:
| geom |
Can be any number of possible values including
“text”, “rect”, “segment”, “curve”, etc. |
| xmin, xmax, ymin, ymax, xend, yend |
Positioning aesthetics where at least one of these
must be defined. |
| … |
Other aesthetics arguments that can be passed along
like color = "red" |
| na.rm |
If FALSE, missing values are removed
with a warning otherwise they are silently removed |
Up to this point we’ve already added some annotations to this plot in
previous lectures. Today we’ll update a few bits of text and lines
segments with arrows instead of boxes.
When naming your geom parameter, you can essentially use
whatever geom_*() are available within ggplot. For
instance, we’ll annotate using a geom_curve() by setting
geom = "curve". Some of the geom_curve()
parameters include:
x, xend, y,
yend: the start and end coordinates of your curve.
lineend: the line end style (round, butt,
square).
curvature: an integer describing the type of
curvature joining start to end.
Negative values produce a left-hand curve.
Positive values produce a right-hand curve.
0 produces a straight line.
angle: an amount (0 to 180) to skew the control
points of the curve.
# Update our phu_window.plot with some annotations and save it to a new object
phu_window_annotate.plot <-
phu_window.plot +
# 2. Aesthetics
# Move our legend to the right side of the panel
theme(legend.justification = c(1,1), legend.position = c(0.98, 0.98)) +
# 3. Scaling
# Stretch out the x-axis scale a bit to fit our labels
scale_x_date(limits = c(as.Date("2020-12-01"), # Set a start date for our limit
as.Date("2023-03-01")), # Identify the last date and use that
date_breaks = "1 month", # How will we break up the dates?
date_labels = "%b-%Y") +
# Winter 2020 lockdown
geom_text(aes(x=as.Date("2020-12-26") + 7, label = "Province-wide lockdown", y=2400),
angle=90, size=10, colour="black") +
annotate("rect", xmin=as.Date("2020-12-26"), xmax=as.Date("2020-12-26") + 14,
ymin=-Inf, ymax=Inf, fill="red", alpha=0.2) +
# Spring 2021 Lockdown
geom_text(aes(x=as.Date("2021-04-03") + 7, label = "Province-wide lockdown", y=2400),
angle=90, size=10, colour="black") +
annotate("rect", xmin=as.Date("2021-04-03"), xmax=as.Date("2021-04-03") + 14,
ymin=-Inf, ymax=Inf, fill="red", alpha=0.2) +
# Omicron arrives
### 3.1.0 Annotate using a curve
geom_text(x=as.Date("2021-09-25"), label = "First Omicron\ncases reported\nin Ontario", y=1000,
hjust=1, vjust = 0, size=10, colour="black") +
annotate("curve", # Make a curve
x=as.Date("2021-10-01"), xend = as.Date("2021-11-28"), # Set the x-coordinates
y=..., yend=..., # Set the y-coordinates
lineend = "round", curvature = ..., # Set the line characteristics
colour="red", linewidth = 1, arrow = ...) + # Add an arrow at the end
# Ontario ends proper PCR testing
geom_text(aes(x=as.Date("2022-02-10"), label = "Ontario reduces public\nPCR COVID-19 testing", y=2500),
hjust=0, size=10, colour="black") +
annotate("segment", x=as.Date("2022-02-01"), xend = as.Date("2021-12-31"),
y=2500, yend=2500, colour="red", linewidth = 1, arrow = arrow())
Error in eval(expr, envir, enclos): '...' used in an incorrect context
# display our plot
phu_window_annotate.plot
Error in eval(expr, envir, enclos): object 'phu_window_annotate.plot' not found
4.2.0 Data labeling with annotations
Unlike the annotations we just discussed, you may wish to directly
label or output information based on your data from the plot. This can
be in the form of error bars, or data labels. Sometimes you may want to
include your sample size or further highlight your outliers.
4.2.1 Label data directly with the directlabels
package
If for some reason you needed to label your plot data directly, the
geom_dl() layer from the directlabels packages
can be quite useful. The package will replace your colour legends with
direct labeling instead. This can (sometimes) be a little cleaner and
less confusing. Parameters you should set when working with
geom_dl() are:
method: this is the positioning method for the
direct label placement and MUST be specified. It passes
parameters from a list on to the
apply.method() function
options include smart.grid, perpendicular.grid, empty.grid,
closest.on.chull, extreme.grid, etc.
find more options here
Use a list() to update additional attributes like
fontsize (cex), fontfamily, rotation (rot) etc.
aes(): like any geom, you can specify aesthetics
information including the labels and
colour.
Note that adding direct labels this way, however, will not remove the
corresponding legend from the plot. It will simply add extra geoms to
your plot. We’ll set our lines to be labeled by the position of their
last.points and if they are closely spaced we will
bumpup the various entries.
Alternatively you can use the last.bumpup method but it
appears to be broken in the current version of directlabels
.
phu_window_annotate.plot +
# Update the labeling of our lines
geom_dl(method=list(...,
# Define "how" we want text ordered
method=...),
aes(label=...))
Error in eval(expr, envir, enclos): object 'phu_window_annotate.plot' not found
4.2.2 Label data using the direct.label() feature
For simplicity, you can also call on direct.label() from
the directlabels package, which will automatically remove
the associated legend from your plot. You can use it by providing the
following parameters:
# Use direct.label() to reformat your plot
...(p = phu_window_annotate.plot, # Provide a plot object
method=list(cex=2, list("last.points", "bumpup"))) # Detail the format information for your labeling
Error in ...(p = phu_window_annotate.plot, method = list(cex = 2, list("last.points", : could not find function "..."
4.3.0 Emphasize your data groups with
gghighlight()
You may find yourself in an instance where you have too many data
groups to present (ie 34 PHUs) but would still like the audience to get
an overview of your dataset while focusing on a few items. As we have
done in the past, you could break groups out using
facet_*() but that isn’t always ideal. We have also
filtered for the top PHUs from a previously generated list but then we
get no sense of the other PHUs at all.
Instead you can use the gghighlight() layer from the
package of the same name. Some helpful parameters from this layer
include:
...: the expressions you will use to filter data (ie
your predicate) which will be passed to
dplyr::filter().
max_highlight: the maximum number of series to
highlight.
unhighlighted_params: the aesthetics for your
unhighlighted groups.
use_group_by: if TRUE, this function will use
dplyr::group_by() to evaluate your
predicate.
use_direct_label: if TRUE, labels will be added
directly to the plot instead of using a legend.
label_key: the column name for label
aesthetics.
label_params: a list of aesthetics customizations
like size.
Let’s plot all of our PHU data onto the graph and only highlight the
top 4 PHUs as before. We’ll have to do some extra fiddling to make it
work just right.
# This is going to be a simpler graph so adjust our plot window size accordingly
options(repr.plot.width=20, repr.plot.height=10)
ggh <-
# Build our plot and save to an object
covid_phu_window.df %>%
# Filter for the top 5 infected PHUs
mutate(public_health_unit = fct_reorder(public_health_unit, window_mean, .desc=TRUE))
phu_cases.plot <-
# redirect the filtered result to ggplot
# 1. Data
ggplot(ggh) +
# 2. Aesthetics
aes(x = start_date, y = window_mean, colour = public_health_unit) +
# Start with a base theme
theme_minimal() +
theme(text = element_text(size=20), # set text size to 20
# Move the legend around to within the panel space
legend.justification = c(1,1),
legend.position = c(0.98,0.98),
# Update the panel to drop the minor axis grid lines
panel.grid.minor = element_blank(),
# Use a black line for the axes
axis.line = element_line(colour = "black"),
axis.text = element_text(colour = "black", face="bold"),
# Adjust the x-axis text
axis.text.x = element_text(angle = 90, # Rotate 90
hjust = 1, # Right-justify
vjust = 0.5) # Centre text "vertically" on axis tick
) +
# Add labels to our plot
labs(title = "Mean cases of COVID-19 in a 14-day window across top 4 Ontario Public Health Units\n",
x = "\nWindow date",
y = "Mean cases in 14-day window\n",
colour = "Public Health Unit",
caption = "*14-day rolling mean with date as start of the window") +
# 3. Scaling
# Start looking at data from July 2020 onwards
scale_x_date(limits = c(as.Date("2020-12-01"), # Set a start date for our limit
as.Date("2023-03-01")), # Set the end date in your limit
date_breaks = "1 month", # How will we break up the dates?
date_labels = "%b-%Y") + # How will we format labels
# Change our y-axis breaks
scale_y_continuous(limits = c(-10, 3500), breaks = seq(0, 3500, 500)) +
### -------------------- Section 4.3.0 highlighting specific geoms -------------------- ###
# 4. Geoms
### Plot all of our public health unit data
...(linewidth=1,
aes(x=start_date,
y=window_mean,
group = public_health_unit,
colour = public_health_unit)) +
### Highlight just the top 4 PHUs
...(public_health_unit %in% phu_by_total_cases_desc[1:4], # Filter your data
use_group_by = FALSE, # Don't group it
label_params = list(size = 10)) + # Set the labels to size 10
### -------------------- Section 4.3.0 highlighting specific geoms -------------------- ###
# 8. Annotations
# Winter 2020 lockdown
geom_text(aes(x=as.Date("2020-12-26") + 7, label = "Province-wide lockdown", y=2400),
angle=90, size=10, colour="black") +
annotate("rect", xmin=as.Date("2020-12-26"), xmax=as.Date("2020-12-26") + 14,
ymin=-Inf, ymax=Inf, fill="red", alpha=0.2) +
# Spring 2021 Lockdown
geom_text(aes(x=as.Date("2021-04-03") + 7, label = "Province-wide lockdown", y=2400),
angle=90, size=10, colour="black") +
annotate("rect", xmin=as.Date("2021-04-03"), xmax=as.Date("2021-04-03") + 14,
ymin=-Inf, ymax=Inf, fill="red", alpha=0.2) +
# Omicron arrives
# Annotate using a curve
geom_text(x=as.Date("2021-09-25"), label = "First Omicron\ncases reported\nin Ontario", y=1000,
hjust=1, vjust = 0, size=10, colour="black") +
annotate("curve", # Make a curve
x=as.Date("2021-10-01"), xend = as.Date("2021-11-28"), # Set the x-coordinates
y=1000, yend=100, # Set the y-coordinates
lineend = "round", curvature = -0.5, # Set the line characteristics
colour="red", linewidth = 1, arrow = arrow()) + # Add an arrow at the end
# Ontario ends proper PCR testing
geom_text(aes(x=as.Date("2022-02-10"), label = "Ontario reduces public\nPCR COVID-19 testing", y=2500),
hjust=0, size=10, colour="black") +
annotate("segment", x=as.Date("2022-02-01"), xend = as.Date("2021-12-31"),
y=2500, yend=2500, colour="red", linewidth = 1, arrow = arrow())
Error in ...(linewidth = 1, aes(x = start_date, y = window_mean, group = public_health_unit, : could not find function "..."
# plot our data
phu_cases.plot
Error in eval(expr, envir, enclos): object 'phu_cases.plot' not found
5.0.0 Annotations and theme alterations through other layers
5.1.0 Annotate error bars with geom_*()
When working with bar or line plots where you may have generated
information such as a mean with standard deviation, you can plot that
information with geom_errorbar(). Unlike annotations from
above this is a specific geom and is treated by the plot like any other
geom_*() we’ve encountered. Under it’s aes()
argument you can specify the ymin and ymax
values or data sources. If you already have generated variables
(columns) for these values, you can use them directly or you can
calculate them on the fly if you have just a mean and standard
deviation.
There are alternative formats of the geom_errorbar() as
well:
| geom_crossbar() |
A hollow box with the middle indicated by a horizonal
line. |
| geom_errorbarh() |
Horizontal versions of the errorbar. |
| geom_linerange() |
Draws an interval using a single vertical line. |
| geom_pointrange() |
Same as a linerange except an additional point is
plotted in the middle of the range. |
Let’s recreate one of our plots from lecture 2 using summary data and
some of these new geoms!
covid_demographics.plot <-
covid_demographics_total.df %>%
# Ungroup this dataframe to clean it up a little
ungroup() %>%
# Filter for cumulative data only
filter(period == "cumulative") %>%
# Select for just the important columns
select(public_health_unit, age_group, percent_cases, percent_hospitalizations) %>%
# Pivot the modified table to capture the "stat_group" of percent_cases vs percent_deaths
pivot_longer(cols=c(3,4), names_to = "stat_group", values_to = "percent_PHU_total") %>%
# Group the data both by age group and then stat group
group_by(age_group, stat_group) %>%
# Generate some summary statistics
summarise(mean = mean(percent_PHU_total),
sd = sd(percent_PHU_total),
median = median(percent_PHU_total)) %>%
# Plot the data as a mixture of multiple geoms
# 1. Data
ggplot(.) +
# 2. Aesthetics
aes(x=age_group, y = mean, linetype=stat_group) +
# Themes
theme_minimal() +
theme(text = element_text(size=20), # set text size to 20
# Update the panel to drop the minor axis grid lines
panel.grid.minor = element_blank(),
# Use a black line for the axes
axis.line = element_line(colour = "black"),
axis.text = element_text(colour = "black", face="bold"),
) +
# Add labels to our plot
# Add labels to the plot
labs(title = "Percent cases and deaths by proportion per PHU across age group",
x = "\nAge group",
y = "Proportion of reported PHU data\n",
caption = "\n*Age group values are calculated as a percentage of total cases or deaths within a PHU") +
# 4. Data
### 5.1.0 Add an errorbar to represent the standard deviation range
...(width = 0.2, aes(y = mean, ymin = ..., ymax = ..., colour=stat_group), size=1) +
### 5.1.0 Add a point to represent the mean of each error bar
geom_point(aes(y=mean, shape=age_group, group = stat_group), size = 5)
[1m[22m`summarise()` has grouped output by 'age_group'. You can override
using the `.groups` argument.
Error in ...(width = 0.2, aes(y = mean, ymin = ..., ymax = ..., colour = stat_group), : could not find function "..."
covid_demographics.plot
Error in eval(expr, envir, enclos): object 'covid_demographics.plot' not found
covid_demographics.plot +
# Add a line to connect our age groups
geom_line(aes(x=age_group, y=..., group = stat_group, colour=stat_group), linewidth=1)
Error in eval(expr, envir, enclos): object 'covid_demographics.plot' not found
Now that we’ve gone and built ourselves an extremely strange plot,
(remember, this is just an example) there are a few things we can
fix/play with.
- You’ll note that we only get 6 shapes plotted for our age
groups.
- The legend titles should be corrected.
- The different legends should be shuffled around in order so that
age_group is on top.
5.2.0 More legend alterations with the guide parameter
or guides() layer
We’ve already looked at some helpful legend alterations pertaining to
positioning and text relabeling in section 2.0.0. Now
we’ll explore some of the remaining tips and tricks when it comes to
working with multiple legends within your plot.
Recall that within each of the guide types, you can update parameters
about text within the legend.
| title |
name, position, theme, hjust, vjust |
| label |
name, position, theme, hjust, vjust |
| key |
width, height |
| order |
you can determine the order of the guide amongst others
using integers [1:99]. 0 sets order by an algorithm |
| other |
direction of guide, number of rows/cols |
We’ll take a closer look at the order parameter next
using our above visualization of the age-grouped data.
covid_demographics.plot +
# 2. Aesthetics
### 5.2.0 Set our guide positions for linetype and colour to 2
guides(linetype = "none",
colour = guide_legend(title="Indicator", order=...)) +
# 3. Scaling
### 5.2.0 rename our x-axis labels using pipes!
scale_x_discrete(labels=covid_demographics_total.df$age_group %>% levels() %>% as.character() %>% # Use string replacement to change our labels
str_replace_all(pattern=" to ", replacement = "-")
) +
### 5.2.0 ggplot only adds 6 shapes automatically so we need to add more manually
# Set values based on number of levels
scale_shape_manual(values=c(1:nlevels(covid_demographics_total.df$age_group)),
# We'll set the age group guide order to 1
guide=guide_legend(title = "Age group", order=...)) +
# Set the colour legend
scale_colour_discrete(name = "Indicator", labels = c("% cases", "% hospitalizations")) +
# 4. Geoms
# Add a line to connect our age groups
geom_line(aes(x=age_group, y=mean, group = stat_group, colour=stat_group), linewidth=1)
Error in eval(expr, envir, enclos): object 'covid_demographics.plot' not found
5.2.1 Force an override to the legend aesthetics with
override.aes
Before we leave the guides() section, we should update
our plot one last time. When you are working with so many shapes, they
can sometimes show up a little smaller than you want. You may wish to
increase their size on the plot but that may disproportionately increase
their size on the legend. If you think about the legend similarly to a
plot itself, then you can grasp how the override.aes
parameter might work.
To adjust some of the aesthetic elements of your plot legend, provide
a named list to the override.aes parameter.
You can use aes parameters like size and
colour to adjust how your legends display information
rather than determining their parameters from the plot itself. We’ll be
applying this parameter within our guides.
At the same time, we’ll update our points to be larger and
bolder/thicker by altering its stroke parameter.
covid_demographics.plot +
# 2. Aesthetics
# Set our guide positions for linetype and colour to 2
guides(linetype = "none",
colour = guide_legend(title="Indicator", order=2)) +
# 3. Scaling
# rename our x-axis labels
scale_x_discrete(labels=covid_demographics_total.df$age_group %>% levels() %>% as.character() %>% # Use string replacement to change our labels
str_replace_all(pattern=" to ", replacement = "-")
) +
# ggplot only adds 6 shapes automatically so we need to add more manually
### 5.2.1 Override the size of the shapes in our legend
# Set values based on number of levels
scale_shape_manual(values=c(1:nlevels(covid_demographics_total.df$age_group)),
# We'll set the age group guide order to 1
guide=guide_legend(title = "Age group", order=1,
# Increase the shape size and line thickness
... = list(...))) +
# Set the colour legend
scale_colour_discrete(name = "Indicator", labels = c("% cases", "% hospitalizations")) +
# 4. Geoms
# Add a line to connect our age groups
geom_line(aes(x=age_group, y=mean, group = stat_group, colour=stat_group), linewidth=1) +
### 5.2.1 Update the points to be larger and thicker
geom_point(aes(y=mean, group = stat_group, shape=age_group),
size = 6, stroke = 1.5)
Error in eval(expr, envir, enclos): object 'covid_demographics.plot' not found
5.3.0 The ggforce package annotates with simple
geom_mark_*() options
The ggforce() package brings helpful geoms and functions
to ggplot2 that can quickly annotate groups of data within
your plots. These layers work with ggplot2 like other
geom_*() layers so you can add them into your plots quite
simply. These objects can also accept aesthetics mappings (including the
ability to filter groups) amongst many other theme-esque parameters and
are added in an automated fashion. More information can be found here
| geom_mark_circle() |
Add circles to all of your data groups |
| geom_mark_rect() |
Add rounded-corner rectangles to your data groups |
| geom_mark_ellipse() |
Add ellipses to all of your data groups |
| geom_mark_hull() |
Add a more tightly-fitted shape/blob (aka hull) around
your data groups |
You can also add custom shapes, specifying their type, location, etc.
and extensions to the facet_*() group of layers allow you
to facet by different columns, zoom in on part of a graph as a facet,
and split facets into multiple plots.
Let’s add some ellipses to our plot and exchange our
geom_line() for a smoother geom_bspline().
More about the geom_bspline() parameters can be found here
demographics_summary.plot <-
covid_demographics_total.df %>%
# Ungroup this dataframe to clean it up a little
ungroup() %>%
# Filter for cumulative data only
filter(period == "cumulative") %>%
# Select for just the important columns
select(public_health_unit, age_group, percent_cases, percent_hospitalizations) %>%
# Pivot the modified table to capture the "stat_group" of percent_cases vs percent_deaths
pivot_longer(cols=c(3,4), names_to = "stat_group", values_to = "percent_PHU_total") %>%
# Group the data both by age group and then stat group
group_by(age_group, stat_group) %>%
# Generate some summary statistics
summarise(mean = mean(percent_PHU_total),
sd = sd(percent_PHU_total),
median = median(percent_PHU_total)) %>%
# Plot the data as a mixture of multiple geoms
# 1. Data
ggplot(.) +
# 2. Aesthetics
aes(x=age_group, y = mean, linetype=stat_group) +
# Themes
theme_minimal() +
theme(text = element_text(size=20), # set text size to 20
# Update the panel to drop the minor axis grid lines
panel.grid.minor = element_blank(),
# Use a black line for the axes
axis.line = element_line(colour = "black"),
axis.text = element_text(colour = "black", face="bold"),
) +
# Add labels to the plot
labs(title = "Percent cases and deaths by proportion per PHU across age group",
x = "\nAge group",
y = "Proportion of reported PHU data\n",
caption = "\n*Age group values are calculated as a percentage of total cases or deaths within a PHU") +
# Set our guide positions for linetype and colour to 2
guides(linetype = "none",
colour = guide_legend(title="Indicator", order=2)) +
# 3. Scaling
# rename our x-axis labels
scale_x_discrete(labels=covid_demographics_total.df$age_group %>% levels() %>% as.character() %>%
# Use string replacement to change our labels
str_replace_all(pattern=" to ", replacement = "-")
) +
# ggplot only adds 6 shapes automatically so we need to add more manually
# Override the size of the shapes in our legend
# Set values based on number of levels
scale_shape_manual(values=c(1:nlevels(covid_demographics_total.df$age_group)),
guide=guide_legend(title = "Age group", order=1,
override.aes = list(size=7, stroke = 0.8))) +
# Set the colour legend
scale_colour_discrete(name = "Indicator", labels = c("% cases", "% hospitalizations")) +
# 4. Data
# Add an errorbar to represent the standard deviation range
geom_errorbar(width = 0.2, aes(y = mean, ymin = mean-sd, ymax = mean+sd, colour=stat_group), size=1) +
# Add a line to connect our age groups
### 5.3.0 replace our line with a bezier line that is a little smoother and goes through most of the points
...(aes(group = stat_group, colour=stat_group), size=1) +
### 5.3.0 Add ellipses to 2 specific age groups to highlight what we care about
...(aes(group = age_group, filter = age_group %in% c("20 to 39", "80+"), label=age_group),
fill="blue", alpha=0.2) +
# Update the points to be larger and thicker
geom_point(aes(y=mean, group = stat_group, shape=age_group), size = 6, stroke = 1.5)
[1m[22m`summarise()` has grouped output by 'age_group'. You can override
using the `.groups` argument.
Error in ...(aes(group = stat_group, colour = stat_group), size = 1): could not find function "..."
# Show the plot
demographics_summary.plot
Error in eval(expr, envir, enclos): object 'demographics_summary.plot' not found
5.4.0 Add data from other sources directly to your
geom_*()
On a side note to annotation, sometimes you want to add a little more
information to your plot. In our case above, we have the summary data
from our plot, but wouldn’t it be nice to add some of the
actual data points to the visualization?
While it may not be the best choice for this particular plot, it’s
still something we can do to demonstrate the importance of layering in
our figures. While we haven’t explicitly discussed this, it should be
clear that by default, each geom_*() draws its data from
the initial dataframe provided to the ggplot() call.
Much like mapping individual aesthetics, we can also assign each
individual geom_*() its own dataset! Recall that last
lecture we introduced the ggbeeswarm package. Let’s add
some datapoints to our last plot by including a
geom_quasirandom() layer. In order to include this data, we
need actual data points so we’ll generate an intermediate dataframe
called covid_demo_long.df.
# Build a long-format dataframe to supply later to our plot
covid_demo_long.df <-
covid_demographics_total.df %>%
# Ungroup this dataframe to clean it up a little
ungroup() %>%
# Filter for cumulative data
filter(period == "cumulative") %>%
# Select for just the important columns
select(public_health_unit, age_group, percent_cases, percent_hospitalizations) %>%
# Pivot the modified table to capture the "stat_group" of percent_cases vs percent_deaths
pivot_longer(cols=c(3,4), names_to = "stat_group", values_to = "percent_PHU_total")
head(covid_demo_long.df)
demographics_summary.plot +
### 5.4.0 Add our points using beeswarm from a DIFFERENT data set
geom_quasirandom(data = ...,
aes(x=age_group, y = percent_PHU_total, group = stat_group),
varwidth=TRUE, method="quasirandom", alpha = 0.5)
Error in eval(expr, envir, enclos): object 'demographics_summary.plot' not found
5.5.0 Working with special characters and symbols in your plot
text
Working in biological science, you will often find yourself wanting
to italicize species names or add special characters when naming
proteins etc. This is not a feat easily accomplished using the options
provided by ggplot2. Instead, you can generate string
objects with the required font-changes or symbols and then provide these
to objects to your plot. In addition to these special text objects, you
could also explore packages that add this kind of functionality more
organically to your plots.
5.5.1 Use the expression() function to generate an
expression object
There are a few routes to accomplish this kind of formatting. We’ll
explore the first, expression() which makes an expression
object. The expression() function interprets a series of
strings and characters into a mathematically-formatted expression. When
supplied as an argument, this object is interpreted as a mathematical
expression and the output is formatted based on a TeX-like set of rules
that parse through the syntax.
Within this function, there are a number of parameters that can
seem like functions but are implemented within
expression() rather than using the base R functions - so
don’t expect the same kind of behaviours. Here is a non-exhaustive list
of potential situations you may encounter.
| +, -, %*%, %/%, %+-% |
basic mathematical symbols for +, -, *, /, and \(\pm\) |
| paste(x,y,z), x*y*z |
juxtapose x, y, and z without any separators |
| sqrt(x) |
square root of x |
| sqrt(x, y) |
the yth root of x |
| plain(x), bold(x), italic(x), bolditalic(x), symbol(x),
underline() |
draw x in normal, bold, italic, bolditalic, symbol and
underlined font |
| list(x, y, z) |
output a comma-separated list of x, y, z |
| hat(x), tilde(x), dot(x), bar(x) |
add symbols above x |
| alpha to omega, Alpha to Omega |
Greek symbols in lower and upper case |
| infinity |
the infinity symbol |
| x ~ y, x ~~ y |
put a space between x and y or put extra space between
them |
| phantom(0) |
leave a gap for “0” without drawing it |
| frac(x, y), over(x, y) |
output x over y |
| atop (x, y) |
output x over y without any bar |
Note from above, to build your expressions from multiple parts, you
should use the * or paste() operators from within
expression().
demographics_summary.plot +
### 5.5.1 alter title labels using the expression() function
labs(title = ...,
x = "\nAge group",
y = "Proportion of reported PHU data\n",
colour = "Public Health Unit",
caption = ...
) +
# Add our points using beeswarm from a DIFFERENT data set
geom_quasirandom(data = covid_demo_long.df,
aes(x=age_group, y = percent_PHU_total, group = stat_group),
varwidth=TRUE, method="quasirandom", alpha = 0.5)
Error in eval(expr, envir, enclos): object 'demographics_summary.plot' not found
5.5.3 Use the ggtext package to create simple markdown
code
As an alternative method to produce simple formatting changes to your
text, the ggtext() package provides improved text rendering
support for ggplot2. While this package only supports a
limited set of Markdown/HTML/CSS syntax, it can handle simple things
like bold and italic text, as well as super- and subscripting.
This package provides 2 new theme() elements:
Both of these elements are meant to effectively replace the
element_text() that is native to ggplot2.
Let’s alter the x- and y-axis text a little bit to see how this works.
Remember we’ll have to replace both our labels and update the
theme() elements we are interested in.
More information on the ggtext package can be found here. Note
that this package has not been updated since June 2020 so caveat
emptor.
# Now build the plot
demographics_summary.plot +
# alter title labels using the expression() function
labs(title = expression("Distribution of"~italic("new cases")~"vs"
~bold("deaths")~"due to COVID-19 across Ontario PHUs"),
### 5.5.3 alter our caption using the bquote() function
... = "***Age*** group<sub>binned when retrieved</sub>",
... = "_Proportion_ __of__ <sup>reported <i>PHU</i> data</sup>",
# alter our caption using the bquote() function
caption = bquote("Errobars represent mean "~ ''%+-%'' ~"standard deviation with n"~">="~.(sample.min))) +
### 5.5.3 Convert the proper theme elements to markdown
theme(axis.title.x = element_markdown(),
axis.title.y = element_markdown()) +
# Add our points using beeswarm from a DIFFERENT data set
geom_quasirandom(data = covid_demo_long.df,
aes(x=age_group, y = percent_PHU_total, group = stat_group),
varwidth=TRUE, method="quasirandom", alpha = 0.5)
Error in eval(expr, envir, enclos): object 'demographics_summary.plot' not found
Which is the best text method for me? As you can see
there are many paths to achieve similar goals. Depending on the
complexity of your needs, you may choose one approach over another.
Overall bquote() is perhaps the most complex to learn
and master but the most flexible since it can also parse
variables as part of its syntax. If you are dealing with simple
math expressions, then the expression() function could
be for you. Utilizing a simpler syntax, it still offers a fair amount of
flexibility for creating mathematical expressions. Lastly, if you want
to do simple modifications to text title format without much need for
equations, then ggtext may be the route to go.
5.6.0 Marginal plots to visualize relationships and distributions
from ggExtra
Marginal plots are a very specialized plot type from the
ggExtra package which combines scatterplot data with
distribution data in the margins. The main plot panel has your two
variables along the x and y axis. Secondary plots are made on the
opposite margins and can be in the form of distribution-based object
ie., histograms, boxplots, etc.
The workhorse of this package is the ggMarginal()
function which takes as input parameters:
p: the ggplot object you would like to add
to
data: optional as the information can be drawn from
p, otherwise it can be a data.frame object of other data
x: the variable name along the x-axis
y: the variable name along the y-axis
type: the type of marginal plot to show - acceptable
types are [density, histogram, boxplot, violin, densigram
(histogram/density plot overlay)]
margins: along which margins to show the plots -
acceptable inputs are [both, x, y]
xparams, yparams: extra parameters to
use only for the x or y marginal plots
groupColour, groupFill: if
TRUE, the colour or fill of the marginal plots will be
mapped to the aesthetics of the scatterplot
Let’s re-imagine our PHU age group data now as a scatterplot with
marginal boxplots. While this won’t be the clearest visualization of
this kind of data it will help to demonstrate how to generate marginal
plots with your data.
# Build our marginal plot from the wider-format that data we have
phu_age_scatter.plot <-
covid_demographics_total.df %>%
filter(age_group %in% c("20 to 39", "40 to 59", "60 to 79", "80+"),
period == "cumulative") %>%
# 1. Data
ggplot(.) +
# 2. Aesthetics
aes(x=percent_cases, y = percent_hospitalizations, colour = age_group) +
# Themes
theme_grey() +
theme(text = element_text(size = 20), # set text size
legend.position = "bottom" # Move our legend to the bottom
) +
# Update the legend so that the legend keys are larger
guides(colour=guide_legend(override.aes= list(size=4))) +
# Update the labels
labs(x = "Percent cases",
y = "Percent hospitalizations",
colour = "Age group") +
# 3. Scaling
scale_colour_viridis_d(option = "viridis") +
# 4. Geoms
...(size = 4, alpha = 0.8) # Add our data points
Error in ...(size = 4, alpha = 0.8): could not find function "..."
# Add our marginal boxplots to our graph
phu_marginal.plot <- ggMarginal(...,
type=..., groupFill=TRUE,
margins="both", size=5)
Error in eval(expr, envir, enclos): '...' used in an incorrect context
# plot our marginal plot
phu_marginal.plot
Error in eval(expr, envir, enclos): object 'phu_marginal.plot' not found
Packages of convenience may come at a cost: While a
package like ggExtra provides a convenient way to
produce marginal plots, it is a pre-packaged function that can be a
little limited. If used correctly, you can make your base plot with all
the changes you need and then add your choice of the available marginal
plots. It should make a fairly good visualization for low effort as long
as you’re happy with its results. Also, this package hasn’t had a major
update since 2018 although small updates and bug fixes appear to be
generated by the creator. For more information, you can check out more
at the ggExtra
cran homepage or go to
the ggExtra GitHub page.
6.0.0 Taking it up a notch
There are many fantastic R packages to analyze and visualize your
data. As a group, we are likely working in a variety of specialized
areas. The plots we have made so far today should be useful for data
exploration for many different kinds of data. In this final section we
are going to learn how to arrange multiple plots per page for those
publication-ready figures.
6.1.0 Multiple plots on one page (ie. for publication images) with
ggarrange()
There are a variety of methods to mix multiple graphs on the same
page, however ggplot2 does not work well with all of them.
I am going to work with a package base that uses gridExtra
(which allows us to arrange plots) but works well with
ggplot2 called ggpubr (which allows us to
align the axes of our plots). For a demonstration, we are going to take
3 plots that we made earlier (phu_cases.plot,
demographics.plot, phu_marginal.plot) and then
arrange and align them in the same figure. (http://www.sthda.com/english/rpkgs/ggpubr/)

Example plot arrangements that can be accomplished with the
ggpubr package.
ggarrange() is a function that takes your plots, their
labels, and how you would like your plots arranged in rows and columns.
To start let’s put our PHU case data (phu_cases.plot) above
our PHU age group data (phu_age.plot). If you picture each
plot as a square in a grid, we need one column (one for each plot,
ncol = 1) and two rows (since they are stacked,
nrow = 2).
# Arrange the two plots in a single page
ggarrange(..., ...,
labels = c("A", "B"),
ncol = ..., nrow = ...)
Error in eval(expr, envir, enclos): '...' used in an incorrect context
6.2.0 Arrange plots within plots
Next we will add in the boxplot by nesting a ggarrange()
call within another.
Imagine a square with 4 boxes.
1. We are going to place our line graph across the top row (top 2
boxes)
2. We’ll place our age group data in the bottom left box
3. We’ll drop our marginal plot into the bottom right box
To do this, we are arranging 2 rows (one with the line graph and one
with the [age group + marginal plot],
nrow = 2) and we are arranging 2 columns in the bottom row
(one with the age group and one with the marginal plot,
ncol = 2).
# Arrange the two plots in a single page
ggarrange(phu_cases.plot, # row 1 plot
# row 2 plots
ggarrange(..., ...,
labels = c("B", "C"),
ncol = 2,
nrow = 1
),
# finish specifying characteristics of the two-row arrangement
labels = c("A"),
ncol = 1,
nrow = 2
)
Error in ggarrange(phu_cases.plot, ggarrange(..., ..., labels = c("B", : object 'phu_cases.plot' not found
6.3.0 Small changes can be made with align and
font()
Okay, there are a few problems with this arrangement.
Problem 1: Spacing aside, our title in plot B has
spread over into area C. If you wanted to keep it, you would have to fix
up the text in the plot and try again. However, we can treat the plots
much like their own data and keep altering them with the +
symbol. That means for a quick fix, we could just remove the
title altogether. Do you remember how to access the plot title?
Problem 2: the x-axes in our B/C plots don’t line up
well. Would it look better if they did? If y-axis lines or x-axis lines
are not aligned, this can be fixed with a call to
align = "v" or align="h".
Problem 3: the font labels denoting each plot look a
little small overall. We can change this aspect with the
font.labels parameter.
If you wanted to make sure all axis titles are the same size you can
specify these small changes using font(). You can try to
access these attributes through simple names like “axis.title”, and
“legend.title” ie font("axis.title", size=9) but you need
to set each graph and each attribute
separately.
Let’s drop our plot B title, and try to shore up the axes between B
and C. Unfortunately we may be stopped by the crowded spacing at the
bottom of these plots.
plot <-
# Arrange the two plots in a single page
ggarrange(phu_cases.plot,
ggarrange(demographics.plot + ..., ### 6.3.0 remove the title
phu_marginal.plot,
labels = c("B", "C"),
ncol = 2,
nrow = 1,
... = list(size=20), # make the labels larger
align = "h" # Try to align the x-axis of both plots
),
labels = c("A"),
ncol = 1,
nrow = 2,
... = list(size=20) # Match the increased label size of the other plots
)
Error in ggarrange(phu_cases.plot, ggarrange(demographics.plot + ..., : object 'phu_cases.plot' not found
plot
function (x, y, ...)
UseMethod("plot")
<bytecode: 0x000000002f2ea030>
<environment: namespace:base>
6.4.0 Determine significance levels for your plots with
ggpubr
One last tool that you might find useful in your plots is the
addition of significance levels or p-values to your plots. Since we’ve
already loaded the ggpubr package, we’ll use a function for
pair-wise comparisons called stat_pwc() which will allow us
to perform a limited analysis of our data.
Before continuing, we should take a look at the
compare_means() function to see how ggpubr
performs its analyses. This function, like other modeling functions (eg
think lm()) can accept a formula based on your variables
from a specific set of data. In our case, we’d like to see how, within
each age group, the percent cases compares to the percent
hospitalizations.
The compare_means() functions has a few relevant
parameters to help us out:
formula: the formula we use to define our dependent
variable as a function of our independent
data: the data set you will be using
method: the type of comparisons you’d like to make
as either comparing means directly (t.test or
wilcox.test) vs omnibus tests (anova or
kruskal.test).
ref.group: a character string or numeric value
denoting which group the other comparisons are to be made against (think
in terms of a control group!)
group.by: a character vector stating which
additional variables you’d like to use in grouping your data. This is
used for grouped plots!
p.adjust.method: how you’d like to correct for
multiple comparisons (eg. bonferroni, hommel, hochberg, BH,
etc)
Let’s try out the compare_means() function on our
COVID-19 demographics data.
covid_demographics_total.df %>%
# Ungroup this dataframe to clean it up a little
ungroup() %>%
# Filter for cumulative data
filter(period == "cumulative") %>%
# Select for just the important columns
select(public_health_unit, age_group, percent_cases, percent_hospitalizations) %>%
# Pivot the modified table to capture the "stat_group" of percent_cases vs percent_hospitalizations
pivot_longer(cols=c(3,4), names_to = "stat_group", values_to = "percent_PHU_total") %>%
# Compare the means of our groups within the data
compare_means(formula = ...,
data = .,
group.by = ...,
p.adjust.method = "hochberg")
Error in covid_demographics_total.df %>% ungroup() %>% filter(period == : '...' used in an incorrect context
6.4.1 Use geom_pcw() to add significance levels to your
plots
Now that we’ve seen how compare_means generates output,
we can use this knowledge to add pairwise comparison significance levels
directly to our plots using the ggplot-friendly layer
geom_pcw() which will essentially annotate our plot with
the levels.
This function shares many of the same parameters as
compare_means() with a few additions:
It does not take a formula but rather generates one based on your
aesthetics mappings of x, y and other
factors.
mapping: the same kind of mapping parameters as all
other geom layers, this let’s us set some aesthetics - most importantly
the group aesthetic.
y.position: the y-axis value at which we want to
display our significance values. This can be a single value or a vector
of values to represent each comparison.
method: here the choice of methods differs and they
come from the rstatix package including
wilcox_test, t_test, dunn_test,
and tukey_hsd
method.args: a list of additional arguments that are
needed for the test method. For instance tukey_hsd will
require a model object (eg lm or aov) to
determine its comparisons.
label: this determines the source of the labels for
your plot. They can include p.adj, p.format,
and p.signif as well as an expression using the syntax we
have already learned.
There are many additional parameters generally for tweaking
how the data is displayed. You can find a list of these over on
the ggpubr
reference page
Let’s add the Wilcoxon comparisons from our above analysis directly
to our grouped violin plots.
# Build and save the plot for later use
demographics.plot <- covid_demographics_total.df %>%
# Ungroup this dataframe to clean it up a little
ungroup() %>%
# Filter for cumulative data
filter(period == "cumulative") %>%
# Select for just the important columns
select(public_health_unit, age_group, percent_cases, percent_hospitalizations) %>%
# Pivot the modified table to capture the "stat_group" of percent_cases vs percent_hospitalizations
pivot_longer(cols=c(3,4), names_to = "stat_group", values_to = "percent_PHU_total") %>%
# filter(stat_group == "percent_cases") %>%
# Plot the data as a grouped violin plot with inset boxplot
# 1. Data
ggplot(.) +
# 2. Aesthetics
aes(x=age_group, y = percent_PHU_total) +
# Start with a base theme
theme_minimal() +
theme(text = element_text(size=20), # set text size to 20
# Move the legend around to within the panel space
legend.justification = c(0,1),
legend.position = c(0.02,0.95),
legend.direction = "horizontal",
# Update the panel to drop the minor axis grid lines
panel.grid.minor = element_blank(),
# Use a black line for the axes
axis.line = element_line(colour = "black"),
axis.text = element_text(colour = "black", face="bold"),
) +
# Add labels to the plot
labs(title = "Percent cases and hospitalizations by proportion per PHU across age group",
x = "\nAge group",
y = "Proportion of reported PHU data\n",
caption = "\n*Age group values are calculated as a percentage of total cases or hospitalizations within a PHU") +
# Use the guides() layer and get rid of the scale_fill_discrete() layer
guides(fill = "none", group = "none") +
# 3. Scaling
scale_y_continuous(limits = c(0, 0.6)) + # Set the limits of our y-axis
# Set the labels of our x-axis categories
scale_x_discrete(labels=c("0-4", "5-11", "12-19", "20-39", "40-59", "60-79", "80+")) +
# Set the colour legend
scale_colour_discrete(name = "Data category", labels = c("% cases", "% hospitalizations")) +
# 4. Data
# multi-factor violin plots but keep the width consistent
# Link your fill to the colour aesthetic
geom_violin(scale="width",
aes(colour = stat_group, fill=after_scale(alpha(colour, 0.3))),
lwd = 1.5) +
# Boxplot but smaller width so they reside "within" the violin plot
geom_boxplot(aes(fill = stat_group), width=0.2,
position = position_dodge(width=0.9),
outlier.shape=NA) + # Remove the outliers
# Add in all of the data points
geom_quasirandom(dodge.width = 0.85, aes(group=stat_group), alpha = 0.8) +
### 6.4.1 Add in signifcance values to your plot
# Set the grouping to use stat_group (like group.by)
geom_pwc(mapping = ...,
# Use a non-parametric test
method = "wilcox_test",
# Label with significance levels instead of p-values
label = ..., label.size = 10,
# Reposition the y-axis location of individual labels
y.position = c(0.2, 0.2, 0.2, 0.45, 0.45, 0.5, 0.5))
Error in eval(expr, envir, enclos): '...' used in an incorrect context
# Show the plot
demographics.plot
Error in eval(expr, envir, enclos): object 'demographics.plot' not found
Now we can simply update our ggarrange plots!
plot <-
# Arrange the two plots in a single page
ggarrange(phu_cases.plot,
ggarrange(demographics.plot + theme(plot.title = element_blank()), ### 6.3.0 remove the title
phu_marginal.plot,
labels = c("B", "C"),
ncol = 2,
nrow = 1,
font.label = list(size=20), # make the labels larger
align = "h" # Try to align the x-axis of both plots
),
labels = c("A"),
ncol = 1,
nrow = 2,
font.label = list(size=20) # Match the increased label size of the other plots
)
Error in ggarrange(phu_cases.plot, ggarrange(demographics.plot + theme(plot.title = element_blank()), : object 'phu_cases.plot' not found
plot
function (x, y, ...)
UseMethod("plot")
<bytecode: 0x000000002f2ea030>
<environment: namespace:base>
7.0.0 Class summary
Today we have dug deep into altering and
playing with our plots to help get them to that extra level. Although
there is far more to explore, this should cover most of your
needs when it comes to cleaning up your plots. To recap, we’ve looked
at:
- Altering themes and element positions.
- Controlling/substituting values and labels.
- Colour palettes.
- Annotating plots with additional geoms.
- Text-based formatting changes.
- Generating Multi-plot figures.
- Annotating plots with statistical analyses.
Looking a little bit ahead at this week’s assignment, you will look
at canada-wide vaccination data.
You now have the tools to create plots like this:

Overall vaccination rates amongst provinces!
7.1.0 Weekly assignment
This week’s assignment will be found under the current lecture folder
under the “assignment” subfolder. It will include an R markdown notebook
that you will use to produce the code and answers for this week’s
assignment. Please provide answers in markdown or code cells that
immediately follow each question section.
| Code |
50% |
- Does it follow best practices? |
|
|
- Does it make good use of available packages? |
|
|
- Was data prepared properly |
| Answers and Output |
50% |
- Is output based on the correct dataset? |
|
|
- Are groupings appropriate |
|
|
- Are correct titles/axes/legends correct? |
|
|
- Is interpretation of the graphs correct? |
Since coding styles and solutions can differ, students are encouraged
to use best practices. Assignments may be rewarded for
well-coded or elegant solutions.
You can save and download the markdown notebook in its native format.
Submit this file to the the appropriate assignment section by 12:59 pm
on the date of our next class: April 4th, 2024.
7.2.0 Acknowledgements
Revision 1.0.0: created and prepared for
CSB1021H S LEC0141, 03-2021 by Calvin Mok,
Ph.D. Bioinformatician, Education and Outreach, CAGEF.
Revision 1.0.1: edited and prepared for
CSB1020H S LEC0141, 03-2022 by Calvin Mok,
Ph.D. Bioinformatician, Education and Outreach, CAGEF.
Revision 1.0.2: edited and prepared for
CSB1020H S LEC0141, 03-2023 by Calvin Mok,
Ph.D. Bioinformatician, Education and Outreach, CAGEF.
Revision 2.0.0: Revised and prepared for
CSB1020H S LEC0141, 03-2024 by Calvin Mok,
Ph.D. Bioinformatician, Education and Outreach, CAGEF.
The Center for the Analysis of Genome Evolution and Function
(CAGEF)
The Centre for the Analysis of Genome Evolution and Function (CAGEF)
at the University of Toronto offers comprehensive experimental design,
research, and analysis services in microbiome and metagenomic studies,
genomics, proteomics, and bioinformatics.
From targeted DNA amplicon sequencing to transcriptomes, whole
genomes, and metagenomes, from protein identification to
post-translational modification, CAGEF has the tools and knowledge to
support your research. Our state-of-the-art facility and experienced
research staff provide a broad range of services, including both
standard analyses and techniques developed by our team. In particular,
we have special expertise in microbial, plant, and environmental
systems.
For more information about us and the services we offer, please visit
https://www.cagef.utoronto.ca/.
LS0tDQp0aXRsZTogJycNCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdA0KICBwZGZfZG9jdW1lbnQ6IGRlZmF1bHQNCi0tLQ0KDQpgYGB7ciwgZWNobz1GQUxTRX0NCiMgVGhpcyBhbGxvd3MgdGhlIGZpbGUgdG8gYmUgTElWRSBhbmQgcnVuIHdpdGhvdXQgZXJyb3JzIHN0b3BwaW5nIGl0Lg0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVycm9yID0gVFJVRSkNCmBgYA0KDQo6Ojoge2FsaWduPSJjZW50ZXIifQ0KPGltZyBzcmM9Imh0dHBzOi8vZ2l0aHViLmNvbS9jYW1vay9DU0JfQ291cnNlX01hdGVyaWFscy9ibG9iL21haW4vQWR2Vml6L0NBR0VGX3NlcnZpY2VzX3NsaWRlLnBuZz9yYXc9dHJ1ZSIgd2lkdGg9IjcwMCIvPg0KOjo6DQoNCiMgQWR2YW5jZWQgR3JhcGhpY3MgYW5kIERhdGEgVmlzdWFsaXphdGlvbiBpbiBSDQoNCiMgTGVjdHVyZSAwMzogYGdncGxvdDJgIGFuZCBhZGRpbmcgdGhvc2UgZmluaXNoaW5nIHRvdWNoZXMNCg0KIyMgMC4xLjAgQW4gb3ZlcnZpZXcgb2YgQWR2YW5jZWQgR3JhcGhpY3MgYW5kIERhdGEgVmlzdWFsaXphdGlvbiBpbiBSDQoNCioqIkFkdmFuY2VkIEdyYXBoaWNzIGFuZCBEYXRhIFZpc3VhbGl6YXRpb24gaW4gUiIqKiBpcyBicm91Z2h0IHRvIHlvdSBieSB0aGUgQ2VudHJlIGZvciB0aGUgQW5hbHlzaXMgb2YgR2Vub21lIEV2b2x1dGlvbiAmIEZ1bmN0aW9uJ3MgKENBR0VGKSBiaW9pbmZvcm1hdGljcyB0cmFpbmluZyBpbml0aWF0aXZlLiBUaGlzIENTQjEwMjEgd2FzIGRldmVsb3BlZCB0byBlbmhhbmNlIHRoZSBza2lsbHMgb2Ygc3R1ZGVudHMgd2l0aCBiYXNpYyBiYWNrZ3JvdW5kcyBpbiBSIGJ5IGZvY3VzaW5nIG9uIGF2YWlsYWJsZSBwaGlsb3NvcGhpZXMsIG1ldGhvZHMsIGFuZCBwYWNrYWdlcyBmb3IgcGxvdHRpbmcgc2NpZW50aWZpYyBkYXRhLiBXaGlsZSB0aGUgZGF0YXNldHMgYW5kIGV4YW1wbGVzIHVzZWQgaW4gdGhpcyBjb3Vyc2Ugd2lsbCBiZSBjZW50cmVkIG9uIFNBUlMtQ29WLTIgZXBpZGVtaW9sb2dpY2FsIGFuZCBnZW5vbWljIGRhdGEsIHRoZSBsZXNzb25zIGxlYXJuZWQgaGVyZWluIHdpbGwgYmUgYnJvYWRseSBhcHBsaWNhYmxlLg0KDQpUaGlzIGxlc3NvbiBpcyB0aGUgdGhpcmQgaW4gYSA2LXBhcnQgc2VyaWVzLiBUaGUgYWltIGZvciB0aGUgZW5kIG9mIHRoaXMgc2VyaWVzIGlzIGZvciBzdHVkZW50cyB0byByZWNvZ25pemUgaG93IHRvIGltcG9ydCwgZm9ybWF0LCBhbmQgZGlzcGxheSBkYXRhIGJhc2VkIG9uIHRoZWlyIGludGVuZGVkIG1lc3NhZ2UgYW5kIGF1ZGllbmNlLiBUaGUgZm9ybWF0IGFuZCBzdHlsZSBvZiB0aGVzZSB2aXN1YWxpemF0aW9ucyB3aWxsIGhlbHAgdG8gaWRlbnRpZnkgYW5kIGNvbnZleSB0aGUga2V5IG1lc3NhZ2UocykgZnJvbSB0aGVpciBleHBlcmltZW50YWwgZGF0YS4NCg0KVGhlIHN0cnVjdHVyZSBvZiB0aGUgY2xhc3MgaXMgYSAqKmNvZGUtYWxvbmcgc3R5bGUqKiBpbiBSIG1hcmtkb3duIG5vdGVib29rcy4gQXQgdGhlIHN0YXJ0IG9mIGVhY2ggbGVjdHVyZSwgc2tlbGV0b24gdmVyc2lvbnMgb2YgdGhlIGxlY3R1cmUgd2lsbCBiZSBwcm92aWRlZCBmb3IgdXNlIG9uIHRoZSBbVW5pdmVyc2l0eSBvZiBUb3JvbnRvIGRhdGF0b29scyBIdWJdKGh0dHBzOi8vZGF0YXRvb2xzLnV0b3JvbnRvLmNhKSBzbyBzdHVkZW50cyBjYW4gcHJvZ3JhbSBhbG9uZyB3aXRoIHRoZSBpbnN0cnVjdG9yLg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgMC4yLjAgTGVjdHVyZSBvYmplY3RpdmVzDQoNCkxhc3Qgd2VlayB3ZSBkaWQgYSBkZWVwIGRpdmUgb24gc29tZSBvZiB0aGUgbW9yZSBwb3B1bGFyIGFuZCBicm9hZGx5IGFwcGxpY2FibGUgdmlzdWFsaXphdGlvbnMgZm9yIGNvbnZleWluZyBiYXNpYyBpZGVhcyBhYm91dCB5b3VyIGRhdGEuIFRoaXMgd2VlayB3aWxsIGZvY3VzIG9uIHRpZHlpbmcgdXAgeW91ciB2aXN1YWxpemF0aW9ucyBhbmQgYWRkaW5nIHRob3NlIGV4dHJhIGZpbmlzaGluZyB0b3VjaGVzIHRoYXQgd2lsbCBoZWxwIHBvbGlzaCB0aGVtIG9mZi4gQWRkaW5nLCByZW1vdmluZywgYWx0ZXJpbmcgZ3JhcGhzLiBHZXR0aW5nIHRoZXNlIGxpdHRsZSBkZXRhaWxzIGNvcnJlY3QgaGVscCB5b3UgdG8gYXZvaWQgYWx0ZXJhdGlvbnMgd2l0aCBhZGRpdGlvbmFsIHNvZnR3YXJlIG91dHNpZGUgb2YgUi4NCg0KQXQgdGhlIGVuZCBvZiB0aGlzIGxlY3R1cmUgeW91IHdpbGwgaGF2ZSBjb3ZlcmVkIHRoZSBmb2xsb3dpbmcgdG9waWNzDQoNCjEuICBBbHRlcmluZyBhbmQgcmVwcm9kdWNpbmcgdGhlbWVzLg0KMi4gIFNldHRpbmcgYW5kIGNoYW5naW5nIHRoZSBjb250ZW50IG9mIHRpdGxlcywgYXhlcywgdGV4dCwgYW5kIGxlZ2VuZHMuDQozLiAgQW5ub3RhdGluZyB3aXRoIHRleHQsIGFuZCBoaWdobGlnaHRpbmcuDQo0LiAgQWx0ZXJpbmcgeW91ciBwbG90IHdpdGggbmV3IGdlb21zLCBhcyB3ZWxsIGFzIGRhdGEvYXhpcy90ZXh0IG1hbmlwdWxhdGlvbnMuDQo1LiAgQXJyYW5naW5nIHBsb3RzIHRvZ2V0aGVyIGluIHRoZSBzYW1lIGZpZ3VyZS4NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDAuMy4wIEEgbGVnZW5kIGZvciB0ZXh0IGZvcm1hdCBpbiBSIG1hcmtkb3duDQoNCmBncmV5IGJhY2tncm91bmRgIC0gYSBwYWNrYWdlLCBmdW5jdGlvbiwgY29kZSwgY29tbWFuZCBvciBkaXJlY3RvcnkuIEJhY2t0aWNrcyBhcmUgYWxzbyB1c2UgZm9yIGluLWxpbmUgY29kZS5cDQoqaXRhbGljcyogLSBhbiBpbXBvcnRhbnQgdGVybSBvciBjb25jZXB0IG9yIGFuIGluZGl2aWR1YWwgZmlsZSBvciBmb2xkZXJcDQoqKmJvbGQqKiAtIGhlYWRpbmcgb3IgYSB0ZXJtIHRoYXQgaXMgYmVpbmcgZGVmaW5lZFwNCltibHVlIHRleHRde3N0eWxlPSJjb2xvcjpibHVlIn0gLSBuYW1lZCBvciB1bm5hbWVkIGh5cGVybGluaw0KDQpgLi4uYCAtIFdpdGhpbiBlYWNoIGNvZGluZyBjZWxsIHRoaXMgd2lsbCBpbmRpY2F0ZSBhbiBhcmVhIG9mIGNvZGUgdGhhdCBzdHVkZW50cyB3aWxsIG5lZWQgdG8gY29tcGxldGUgZm9yIHRoZSBjb2RlIGNlbGwgdG8gcnVuIGNvcnJlY3RseS4NCg0KOjo6IHsuYWxlcnQgLmFsZXJ0LWJsb2NrIC5hbGVydC1pbmZvfQ0KKipCbHVlIGJveDoqKiBBIGtleSBjb25jZXB0IHRoYXQgaXMgYmVpbmcgaW50cm9kdWNlZA0KOjo6DQoNCjo6OiB7LmFsZXJ0IC5hbGVydC1ibG9jayAuYWxlcnQtd2FybmluZ30NCioqWWVsbG93IGJveDoqKiBSaXNrIG9yIGNhdXRpb24NCjo6Og0KDQo6Ojogey5hbGVydCAuYWxlcnQtYmxvY2sgLmFsZXJ0LXN1Y2Nlc3N9DQoqKkdyZWVuIGJveGVzOioqIFJlY29tbWVuZGVkIHJlYWRzIGFuZCByZXNvdXJjZXMgdG8gbGVhcm4gUHl0aG9uDQo6OjoNCg0KOjo6IHsuYWxlcnQgLmFsZXJ0LWJsb2NrIC5hbGVydC1kYW5nZXJ9DQoqKlJlZCBib3hlczoqKiBBIGNvbXByZWhlbnNpb24gcXVlc3Rpb24gd2hpY2ggbWF5IG9yIG1heSBub3QgaW52b2x2ZSBhIGNvZGluZyBjZWxsLiBZb3UgdXN1YWxseSBmaW5kIHRoZXNlIGF0IHRoZSBlbmQgb2YgYSBzZWN0aW9uLg0KOjo6DQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyAwLjQuMCBMZWN0dXJlIGFuZCBkYXRhIGZpbGVzIHVzZWQgaW4gdGhpcyBjb3Vyc2UNCg0KIyMjIDAuNC4xIFdlZWtseSBMZWN0dXJlIGFuZCBza2VsZXRvbiBmaWxlcw0KDQpFYWNoIHdlZWssIG5ldyBsZXNzb24gZmlsZXMgd2lsbCBhcHBlYXIgd2l0aGluIHlvdXIgUlN0dWRpbyBmb2xkZXJzLiBXZSBhcmUgcHVsbGluZyBmcm9tIGEgR2l0SHViIHJlcG9zaXRvcnkgdXNpbmcgdGhpcyBbUmVwb3NpdG9yeSBnaXQtcHVsbCBsaW5rXShodHRwczovL3IuZGF0YXRvb2xzLnV0b3JvbnRvLmNhL2h1Yi91c2VyLXJlZGlyZWN0L2dpdC1wdWxsP3JlcG89aHR0cHMlM0ElMkYlMkZnaXRodWIuY29tJTJGY2Ftb2slMkYyMDI0LTAzLUFkdl9HcmFwaGljc19SJnVybHBhdGg9cnN0dWRpbyUyRiZicmFuY2g9bWFpbikuIFNpbXBseSBjbGljayBvbiB0aGUgbGluayBhbmQgaXQgd2lsbCB0YWtlIHlvdSB0byB0aGUgW1VuaXZlcnNpdHkgb2YgVG9yb250byBkYXRhdG9vbHMgSHViXShodHRwczovL2RhdGF0b29scy51dG9yb250by5jYSkuIFlvdSB3aWxsIG5lZWQgdG8gdXNlIHlvdXIgVVRPUmlkIGNyZWRlbnRpYWxzIHRvIGNvbXBsZXRlIHRoZSBsb2dpbiBwcm9jZXNzLiBGcm9tIHRoZXJlIHlvdSB3aWxsIGZpbmQgZWFjaCB3ZWVrJ3MgbGVjdHVyZSBmaWxlcyBpbiB0aGUgZGlyZWN0b3J5IGAvMjAyNC0wMy1BZHZfR3JhcGhpY3NfUi9MZWN0dXJlX1hYYC4gWW91IHdpbGwgZmluZCBhIHBhcnRpYWxseSBjb2RlZCBgc2tlbGV0b24uUm1kYCBmaWxlIGFzIHdlbGwgYXMgYWxsIG9mIHRoZSBkYXRhIGZpbGVzIG5lY2Vzc2FyeSB0byBydW4gdGhlIHdlZWsncyBsZWN0dXJlLg0KDQpBbHRlcm5hdGl2ZWx5LCB5b3UgY2FuIGRvd25sb2FkIHRoZSBSLU1hcmtkb3duIE5vdGVib29rIChgLlJtZGApIGFuZCBkYXRhIGZpbGVzIGZyb20gdGhlIFJTdHVkaW8gc2VydmVyIHRvIHlvdXIgcGVyc29uYWwgY29tcHV0ZXIgaWYgeW91IHdvdWxkIGxpa2UgdG8gcnVuIGluZGVwZW5kZW50bHkgb2YgdGhlIFRvcm9udG8gdG9vbHMuDQoNCiMjIyAwLjQuMiBMaXZlLWNvZGluZyBIVE1MIHBhZ2UNCg0KQSBsaXZlIGxlY3R1cmUgdmVyc2lvbiB3aWxsIGJlIGF2YWlsYWJsZSBhdCBbY2Ftb2suZ2l0aHViLmlvXShodHRwczovL2NhbW9rLmdpdGh1Yi5pby8yMDI0LTAzLkFkdl9HcmFwaGljc19SL2luZGV4Lmh0bWwpIHRoYXQgd2lsbCB1cGRhdGUgYXMgdGhlIGxlY3R1cmUgcHJvZ3Jlc3Nlcy4gQmUgc3VyZSB0byByZWZyZXNoIHRvIHRha2UgYSBsb29rIGlmIHlvdSBnZXQgbG9zdCENCg0KIyMjIDAuNC4zIFBvc3QtbGVjdHVyZSBQREZzDQoNCkFzIG1lbnRpb25lZCBhYm92ZSwgYXQgdGhlIGVuZCBvZiBlYWNoIGxlY3R1cmUgdGhlcmUgd2lsbCBiZSBhIGNvbXBsZXRlZCB2ZXJzaW9uIG9mIHRoZSBsZWN0dXJlIGNvZGUgcmVsZWFzZWQgYXMgYSBQREYgZmlsZSB1bmRlciB0aGUgTW9kdWxlcyBzZWN0aW9uIG9mIFF1ZXJjdXMuDQoNCiMjIyAwLjQuNCBEYXRhIHVzZWQgaW4gdGhpcyBsZXNzb24NCg0KVG9kYXkncyBkYXRhc2V0cyB3aWxsIGZvY3VzIG9uIGEgbnVtYmVyIG9mIGRhdGFzZXRzIHdlJ3ZlIHVzZWQgaW4gb3VyIHByZXZpb3VzIGxlY3R1cmVzLg0KDQojIyMgMC40LjQuMSBEYXRhc2V0IDE6IExlY3R1cmUwMy5SRGF0YQ0KDQpUaGlzIGRhdGEgZmlsZSBjb250YWlucyA0IG9iamVjdHM6DQoNCjEuICBgY292aWRfcGh1X2xvbmcuZGZgOiBDT1ZJRC0xOSBkYWlseSBjYXNlcyB2YWx1ZXMgYWNyb3NzIE9udGFyaW8gcHVibGljIGhlYWx0aCB1bml0cyBzZWVuIGluIGxlY3R1cmUgMDEuDQoNCjIuICBgY292aWRfcGh1X3dpbmRvdy5kZmA6IHNsaWRpbmcgd2luZG93IGRhdGEgZ2VuZXJhdGVkIGZyb20gYGNvdmlkX3BodV9sb25nLmRmYCBiYXNlZCBvbiBhIDE0LWRheSByb2xsaW5nIG1lYW4uDQoNCjMuICBgcGh1X2J5X3RvdGFsX2Nhc2VzX2Rlc2NgOiBhIGxpc3Qgb2YgT250YXJpbyBQSFVzIGluIGRlc2NlbmRpbmcgb3JkZXIgYnkgY2FzZWxvYWQNCg0KNC4gIGBjb3ZpZF9kZW1vZ3JhcGhpY3NfdG90YWwuZGZgOiBhZ2UgZ3JvdXAgZGVtb2dyYXBoaWNzIGluIGEgbG9uZy1mb3JtYXQgdGhhdCB3ZSBnZW5lcmF0ZWQgaW4gbGVjdHVyZSAwMi4NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDAuNS4wIFBhY2thZ2VzIHVzZWQgaW4gdGhpcyBsZXNzb24NCg0KYHRpZHl2ZXJzZWAgd2hpY2ggaGFzIGEgbnVtYmVyIG9mIHBhY2thZ2VzIGluY2x1ZGluZyBgZHBseXJgLCBgdGlkeXJgLCBgc3RyaW5ncmAsIGBmb3JjYXRzYCBhbmQgYGdncGxvdDJgDQoNCmB2aXJpZGlzYCBoZWxwcyB0byBjcmVhdGUgY29sb3ItYmxpbmQgcGFsZXR0ZXMgZm9yIG91ciBkYXRhIHZpc3VhbGl6YXRpb25zDQoNCmBsdWJyaWRhdGVgIGFuZCBgem9vYCBhcmUgaGVscGVyIHBhY2thZ2VzIHVzZWQgZm9yIHdvcmtpbmcgd2l0aCBkYXRlIGZvcm1hdHMgaW4gUg0KDQpgZ2d0aGVtZXNgLCBgZGlyZWN0bGFiZWxzYCwgYGdnZm9yY2VgLCBgZ2diZWVzd2FybWAsIGBnZ2hpZ2hsaWdodGAsIGFuZCBgZ2dFeHRyYWAgd2lsbCBwcm92aWRlIHVzIG5ldyBnZW9tcyBhbmQgbWV0aG9kcyBmb3IgcGxvdHRpbmcgb3IgYWx0ZXJpbmcgaG93IG91ciBwbG90cyBsb29rLg0KDQpgZ2dwdWJyYCBmb3IgYXJyYW5naW5nIG91ciBwbG90cy4NCg0KYGBge3J9DQojIE5vbmUgb2YgdGhlc2UgcGFja2FnZXMgYXJlIGFscmVhZHkgYXZhaWxhYmxlIG9uIHIuZGF0YXRvb2xzDQojIGluc3RhbGwucGFja2FnZXMoImdndGhlbWVzIiwgZGVwZW5kZW5jaWVzID0gVFJVRSkNCiMgaW5zdGFsbC5wYWNrYWdlcygiZGlyZWN0bGFiZWxzIiwgZGVwZW5kZW5jaWVzID0gVFJVRSkNCiMgaW5zdGFsbC5wYWNrYWdlcygiZ2dmb3JjZSIsIGRlcGVuZGVuY2llcyA9IFRSVUUpDQojIGluc3RhbGwucGFja2FnZXMoImdnYmVlc3dhcm0iLCBkZXBlbmRlbmNpZXMgPSBUUlVFKQ0KIyBpbnN0YWxsLnBhY2thZ2VzKCJnZ2hpZ2hsaWdodCIsIGRlcGVuZGVuY2llcyA9IFRSVUUpDQojIGluc3RhbGwucGFja2FnZXMoImdnRXh0cmEiLCBkZXBlbmRlbmNpZXMgPSBUUlVFKQ0KIyBpbnN0YWxsLnBhY2thZ2VzKCJnZ3B1YnIiLCBkZXBlbmRlbmNpZXMgPSBUUlVFKQ0KIyBpbnN0YWxsLnBhY2thZ2VzKCJnZ3RleHQiLCBkZXBlbmRlbmNpZXMgPSBUUlVFKQ0KYGBgDQoNCmBgYHtyfQ0KIyBQYWNrYWdlcyB0byBoZWxwIHRpZHkgb3VyIGRhdGENCmxpYnJhcnkodGlkeXZlcnNlKQ0KDQojIFBhY2thZ2VzIGZvciB0aGUgZ3JhcGhpY2FsIGFuYWx5c2lzIHNlY3Rpb24NCmxpYnJhcnkodmlyaWRpcykNCg0KIyBOZXcgdmlzdWFsaXNhdGlvbiBwYWNrYWdlcw0KbGlicmFyeShnZ3RoZW1lcykNCmxpYnJhcnkoZGlyZWN0bGFiZWxzKQ0KbGlicmFyeShnZ2ZvcmNlKQ0KbGlicmFyeShnZ2JlZXN3YXJtKQ0KbGlicmFyeShnZ2hpZ2hsaWdodCkNCmxpYnJhcnkoZ2dFeHRyYSkNCmxpYnJhcnkoZ2dwdWJyKQ0KbGlicmFyeShnZ3RleHQpDQoNCiMgcGFja2FnZXMgdXNlZCBmb3Igd29ya2luZyB3aXRoL2Zvcm1hdGluZyBkYXRlcyBpbiBSDQpsaWJyYXJ5KGx1YnJpZGF0ZSkgDQpsaWJyYXJ5KHpvbykNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyAxLjAuMCBQcmVzZW50IHlvdXIgZGF0YSBpbiBpdHMgYmVzdCBmb3JtYXQgYW5kICpmb3JtKg0KDQpMYXN0IHdlZWsgaW4gbGVjdHVyZSAyIHdlIHNwZW50IG91ciB0aW1lIGhpZ2hsaWdodGluZyB2YXJpb3VzIHR5cGVzIG9mIHBsb3RzIGFuZCB0aGVpciB2YXJpYW50cyB3aGlsZSBkaXNjZXJuaW5nIHRoZSBwcm9wZXIgY2lyY3Vtc3RhbmNlcyBvZiB0aGVpciB1c2UuIE5vdyB0aGF0IHdlIGtub3cgd2hpY2ggcGxvdHMgdG8gdXNlIGFuZCB3aGVuIHRvIHVzZSB0aGVtLCB3ZSBjYW4gZm9jdXMgb24gaG93IHRvIGNsZWFuIHVwIHlvdXIgdmlzdWFsaXphdGlvbnMgc28gZWFjaCBjYW4gYmUgcHJlc2VudGVkIGFzIGl0cyAiYmVzdCBzZWxmIi4NCg0KVGhyb3VnaCBib3RoIGxlY3R1cmVzIGFuZCBhc3NpZ25tZW50cyB3ZSBoYXZlIGFscmVhZHkgZ2xpbXBzZWQgYXQgc29tZSBvZiB0aGUgY29tbWFuZHMgYW5kIGxheWVycyB3ZSBjYW4gdXNlIHRvIGltcHJvdmUgdXBvbiBvdXIgZ3JhcGhzIHdoZXRoZXIgdGhhdCBpcyBieSBjaG9vc2luZyBjb2xvdXIsIHRpdGxlcywgb3IgbGVnZW5kIGluZm9ybWF0aW9uLiBUb2RheSB3ZSdsbCBleHBsb3JlIHRob3NlIG9wdGlvbnMgbW9yZSBkZWVwbHkgc28geW91IGRvbid0IGhhdmUgdG8gc3BlbmQgZGF5cyB0cnlpbmcgdG8gZ2V0IHlvdXIgdmlzdWFsaXphdGlvbnMgdG8gbG9vayBwZXJmZWN0LiBXZSdsbCByZXZpc2l0IHNvbWUgb2xkIHBsb3RzIGFuZCBidWlsZCB0aGVtIHVwIGZyb20gYmFzaWNzIGFuZCB0d2VhayB0aGVtIHRvIHByb2R1Y2UgdGhpczoNCg0KOjo6IHthbGlnbj0iY2VudGVyIn0NCjxpbWcgc3JjPSJodHRwczovL2dpdGh1Yi5jb20vY2Ftb2svQ1NCX0NvdXJzZV9NYXRlcmlhbHMvYmxvYi9tYWluL0FkdlZpei9MZWMwMy5nZ2FycmFuZ2UucG5nP3Jhdz10cnVlIiB3aWR0aD0iMTAwMCIvPg0KDQpCeSB0aGUgdGltZSB3ZSBmaW5pc2ggdG9kYXksIHdlJ2xsIGtub3cgaG93IHRvIG1hbmlwdWxhdGUgbWFueSBvZiB0aGUgZWxlbWVudHMgb2YgYSBnZ3Bsb3QuDQo6OjoNCg0KTGV0J3Mgc3RhcnQgd2l0aCBvdXIgUEhVIGNhc2Vsb2FkIGRhdGEgZnJvbSBsZWN0dXJlIDEuIFdlJ2xsIGxvYWQgaXQgZnJvbSBhIGAuUkRhdGFgIGZpbGUgYWxvbmcgd2l0aCBzb21lIG90aGVyIGhlbHBmdWwgb2JqZWN0cy4NCg0KYGBge3J9DQojIExvYWQgc29tZSBwcmVnZW5lcmF0ZWQgZGF0YSB0YWJsZXMgZm9yIGNsYXNzDQojIExvYWQgTGVjdHVyZTAzLlJEYXRhIA0KbG9hZCgiZGF0YS9MZWN0dXJlMDMuUkRhdGEiKQ0KbHMoKQ0KYGBgDQoNCmBgYHtyfQ0KIyBSZW1pbmQgb3Vyc2VsdmVzIHdoYXQgY292aWRfcGh1X3dpbmRvdy5kZiBsb29rcyBsaWtlDQpoZWFkKGNvdmlkX3BodV93aW5kb3cuZGYpDQpgYGANCg0KYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD0xMH0NCg0KY292aWRfcGh1X3dpbmRvdy5kZiAlPiUgDQogICMgRmlsdGVyIGZvciB0aGUgdG9wIDUgaW5mZWN0ZWQgUEhVcw0KICBmaWx0ZXIocHVibGljX2hlYWx0aF91bml0ICVpbiUgcGh1X2J5X3RvdGFsX2Nhc2VzX2Rlc2NbMTo1XSwNCiAgICAgICAgIHN0YXJ0X2RhdGUgPj0gYXMuRGF0ZSgiMjAyMC0xMi0wMSIpKSAlPiUgDQoNCiAgIyByZWRpcmVjdCB0aGUgZmlsdGVyZWQgcmVzdWx0IHRvIGdncGxvdA0KICAjIDEuIERhdGENCiAgZ2dwbG90KC4pICsNCiAgICAjIDIuIEFlc3RoZXRpY3MNCiAgICBhZXMoeCA9IHN0YXJ0X2RhdGUsIHkgPSB3aW5kb3dfbWVhbiwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgU2V0IG91ciB4IGFuZCB5IGF4ZXMNCiAgICAgICAgY29sb3VyID0gZmN0X3Jlb3JkZXIocHVibGljX2hlYWx0aF91bml0LCB3aW5kb3dfbWVhbiwgLmRlc2M9VFJVRSkpICsgICMgUmVvcmRlciBvdXIgUEhVcw0KDQogICAgIyA0LiBHZW9tcw0KICAgIGdlb21fbGluZShsaW5ld2lkdGg9MSkNCmBgYA0KDQpGcm9tIG91ciBhYm92ZSBwbG90LCB3ZSBjYW4gaW1tZWRpYXRlbHkgc2VlIHRoYXQgd2UgaGF2ZSBpc3N1ZXMgdGhhdCBuZWVkIHJlbWVkeWluZzoNCg0KMS4gIFRoZSBvdmVyYWxsIGZvbnQgc2l6ZSBvZiB0aGUgcGxvdCBpcyBzbWFsbCAoYW5kIEkgaGF2ZSBvbGQgZXllcykuDQoyLiAgVGhlIGxlZ2VuZCB0aXRsZSBpcyBxdWl0ZSBsYXJnZSBhbmQgYmFzZWQgc3BlY2lmaWNhbGx5IG9uIHRoZSBgYWVzKClgIGFzc2lnbm1lbnQgdXNlZC4NCjMuICBPdXIgYXhlcyBuYW1lcyBuZWVkIHRvIGJlIHVwZGF0ZWQgYW5kIHdlIGNvdWxkIHVzZSBhIHRpdGxlIHRvby4gV2UnbGwgZml4IHRoaXMgcHJvYmxlbSBsYXRlciENCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDEuMS4wIENvbnRyb2wgdGhlIGRpc3BsYXkgb2YgYWxsIG5vbi1kYXRhIGVsZW1lbnRzIHdpdGggYHRoZW1lKClgDQoNCkFsdGhvdWdoIHdlIGhhdmVuJ3QgZGlyZWN0bHkgZGlzY3Vzc2VkIHRoZW1lcyB5ZXQsIHdlIGhhdmUgc2VlbiBpdCBhcHBlYXJpbmcgaGVyZSBhbmQgdGhlcmUgaW4gb3VyIGluZGl2aWR1YWwgcGxvdHMuIFRoZSBpbmZsdWVuY2Ugb2YgdGhlbWVzIHNldHMgYW5kIGNvbnRyb2xzIHRoZSAqcHJlc2VudGF0aW9uKiBvZiB0aXRsZXMsIGxhYmVscywgdGV4dCwgYmFja2dyb3VuZCwgbGVnZW5kcywgZXRjLiBZb3UgZG9uJ3QgZGlyZWN0bHkgY2hhbmdlIHRoZSBhY3R1YWwgaW5mb3JtYXRpb24gcHJlc2VudGVkIGluIHRoZXNlIGVsZW1lbnRzLg0KDQpDYWxscyB0byBgdGhlbWUoKWAgZ2VuZXJhbGx5IHRha2UgdGhlIGZvcm0gb2YgYHRoZW1lKGVsZW1lbnQuY29tcG9uZW50LnN1Yi1jb21wb25lbnQgPSBlbGVtZW50XyoocGFyYW1ldGVyID0gdmFsdWUpKWANCg0KU29tZSBiYXNpYyBlbGVtZW50cyBpbmNsdWRlIGxpbmUsIHJlY3QsIHRleHQsIHRpdGxlLCBhbmQgYXNwZWN0LnJhdGlvLiBBbHRlcmluZyB0aGVzZSBlbGVtZW50cyBpbiBgdGhlbWUoKWAgd2lsbCBhbHRlciBhbGwgZWxlbWVudHMgb2YgdGhlaXIga2luZCAoaWUgYWxsIGxpbmVzLCByZWN0YW5nbGVzLCB0ZXh0IGV0Yy4pLiBBbHRlcm5hdGl2ZWx5IHNwZWNpZmljIGVsZW1lbnQgY29tcG9uZW50cyBjYW4gYmUgYWx0ZXJlZCBtb3JlIGRpcmVjdGx5LiBUaGUgZm9sbG93aW5nIHRhYmxlIGxpc3RzIG1vc3Qgb2YgdGhlIHBvc3NpYmxlIHRoZW1lIGVsZW1lbnRzIGFuZCBjb21wb25lbnRzLiBUaGV5IGNhbiBiZSBhcyBzcGVjaWZpYyBhcyBgYXhpcy50aXRsZS54LnRvcGAuIE1vcmUgZGV0YWlsZWQgZGVzY3JpcHRpb25zIGNhbiBiZSBmb3VuZCBbaGVyZV0oaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL3RoZW1lLmh0bWwpLg0KDQp8IEVsZW1lbnQgfCBEZXNjcmlwdGlvbiAgICAgICAgICAgICAgfCBDb21wb25lbnRzICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IFN1Yi1jb21wb25lbnRzICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgT3RoZXIgICAgICAgICAgICAgICAgICAgIHwNCnw6LS0tLS0tLS0tLS0tLS18Oi0tLS0tLS0tLS0tLS0tfDotLS0tLS0tLS0tLS0tLXw6LS0tLS0tLS0tLS0tLS18Oi0tLS0tLS0tLS0tLS0tfA0KfCBheGlzICAgIHwgeCBhbmQgeSBheGlzIGVsZW1lbnRzICAgIHwgdGl0bGUsIHRleHQsIHRpY2tzLCBsaW5lICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCB4LCB5LCBsZW5ndGggICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IHRvcCwgYm90dG9tLCBsZWZ0LCByaWdodCB8DQp8IGxlZ2VuZCAgfCBhbGwgbGVnZW5kIGVsZW1lbnRzICAgICAgfCBiYWNrZ3JvdW5kLCBtYXJnaW4sIHNwYWNpbmcsIGtleSwgdGV4dCwgdGl0bGUsIHBvc2l0aW9uLCBkaXJlY3Rpb24sIGp1c3RpZmljYXRpb24sIGJveCB8IHgsIHksIHNpemUsIGhlaWdodCwgd2lkdGgsIGFsaWduLCBqdXN0LCBzcGFjaW5nIHwgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgcGFuZWwgICB8IGJhY2tncm91bmQgcGxvdHRpbmcgYXJlYSB8IGJhY2tncm91bmQsIGJvcmRlciwgc3BhY2luZywgZ3JpZCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgeCwgeSwgbWFqb3IsIG1pbm9yICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBwbG90ICAgIHwgZW50aXJlIHBsb3QgICAgICAgICAgICAgIHwgYmFja2dyb3VuZCwgdGl0bGUsIHN1YnRpdGxlLCBjYXB0aW9uLCB0YXgsIG1hcmdpbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBwb3NpdGlvbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IHN0cmlwICAgfCBmYWNldCBsYWJlbHMgICAgICAgICAgICAgfCBiYWNrZ3JvdW5kLCBwbGFjZW1lbnQsIHRleHQsIHN3aXRjaCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IHgsIHksIHRleHQsIHBhZCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgZ3JpZCwgd3JhcCAgICAgICAgICAgICAgIHwNCg0KWW91IHVwZGF0ZSBvciBzZXQgeW91ciBpbmRpdmlkdWFsIGVsZW1lbnRzIHVzaW5nIHRoZSBgZWxlbWVudF8qKClgIGZ1bmN0aW9ucy4gV2l0aGluIGVhY2ggZWxlbWVudCB5b3UgY2FuIHR5cGljYWxseSBjb250cm9sIGFlc3RoZXRpY3MgbGlrZSBmaWxsLCBjb2xvdXIvY29sb3IsIHNpemUsIGV0Yy4gQmVsb3cgaXMgYSBzdW1tYXJ5IG9mIHRoZSBlbGVtZW50cyBvZiBjb25jZXJuIGFuZCB0aGVpciBwYXJhbWV0ZXJzLiBTcGVjaWZpYyBgZWxlbWVudHNfKigpYCB3aWxsIGNvcnJlc3BvbmQgd2l0aCB0aGUgYWJvdmUgYHRoZW1lYCBlbGVtZW50cy4NCg0KfCBlbGVtZW50IGNhbGwgICAgfCBkZXNjcmlwdGlvbiAgICAgICAgICAgICAgICAgICAgICAgICB8ICAgICBmaWxsICAgICB8ICAgIGNvbG91ciAgICB8ICAgICBzaXplICAgICB8ICAgbGluZXR5cGUgICB8ICAgbGluZWVuZCAgICB8ICAgIGFycm93ICAgICB8ICAgIGZhbWlseSAgICB8ICAgICBmYWNlICAgICB8ICAgIGhqdXN0ICAgICB8ICAgIHZqdXN0ICAgICB8ICAgIGFuZ2xlICAgICB8ICBsaW5laGVpZ2h0ICB8ICAgIG1hcmdpbiAgICB8DQp8Oi0tLS18Oi0tLS18Oi0tLTp8Oi0tLTp8Oi0tLTp8Oi0tLTp8Oi0tLTp8Oi0tLTp8Oi0tLTp8Oi0tLTp8Oi0tLTp8Oi0tLTp8Oi0tLTp8Oi0tLTp8Oi0tLTp8DQp8IGVsZW1lbnRfbGluZSgpICB8IGZvcm1hdHRpbmcgb2YgbGluZXMgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgIHwgJFxjaGVja21hcmskIHwgJFxjaGVja21hcmskIHwgJFxjaGVja21hcmskIHwgJFxjaGVja21hcmskIHwgJFxjaGVja21hcmskIHwgICAgICAgICAgICAgIHwgICAgICAgICAgICAgIHwgICAgICAgICAgICAgIHwgICAgICAgICAgICAgIHwgICAgICAgICAgICAgIHwgICAgICAgICAgICAgIHwgICAgICAgICAgICAgIHwNCnwgZWxlbWVudF90ZXh0KCkgIHwgZm9ybWF0dGluZyBvZiB0ZXh0ICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgfCAkXGNoZWNrbWFyayQgfCAkXGNoZWNrbWFyayQgfCAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgfCAkXGNoZWNrbWFyayQgfCAkXGNoZWNrbWFyayQgfCAkXGNoZWNrbWFyayQgfCAkXGNoZWNrbWFyayQgfCAkXGNoZWNrbWFyayQgfCAkXGNoZWNrbWFyayQgfCAkXGNoZWNrbWFyayQgfA0KfCBlbGVtZW50X3JlY3QoKSAgfCBib3JkZXJzIGFuZCBiYWNrZ3JvdW5kICAgICAgICAgICAgICB8ICRcY2hlY2ttYXJrJCB8ICRcY2hlY2ttYXJrJCB8ICRcY2hlY2ttYXJrJCB8ICRcY2hlY2ttYXJrJCB8ICAgICAgICAgICAgICB8ICAgICAgICAgICAgICB8ICAgICAgICAgICAgICB8ICAgICAgICAgICAgICB8ICAgICAgICAgICAgICB8ICAgICAgICAgICAgICB8ICAgICAgICAgICAgICB8ICAgICAgICAgICAgICB8ICAgICAgICAgICAgICB8DQp8IGVsZW1lbnRfYmxhbmsoKSB8IGRyYXdzIG5vdGhpbmcsIGFuZCBhc3NpZ25zIG5vIHNwYWNlIHwgICAgICAgICAgICAgIHwgICAgICAgICAgICAgIHwgICAgICAgICAgICAgIHwgICAgICAgICAgICAgIHwgICAgICAgICAgICAgIHwgICAgICAgICAgICAgIHwgICAgICAgICAgICAgIHwgICAgICAgICAgICAgIHwgICAgICAgICAgICAgIHwgICAgICAgICAgICAgIHwgICAgICAgICAgICAgIHwgICAgICAgICAgICAgIHwgICAgICAgICAgICAgIHwNCg0KYGluaGVyaXQuYmxhbmtgIGlzIGFuIGFkZGl0aW9uYWwgcGFyYW1ldGVyIHlvdSBjYW4gdXNlIGluIHRoZXNlIGZ1bmN0aW9ucyB0aGF0IGlzIG5vcm1hbGx5IHNldCB0byBgRkFMU0VgLiBXaGVuIHNldCB0byBgVFJVRWAsIGlmIGEgcGFyZW50YWwgbGF5ZXIgdXNlcyBgZWxlbWVudF9ibGFuaygpYCwgaXQgd2lsbCBjYXVzZSB0aGlzIGVsZW1lbnQgdG8gYmUgYmxhbmsgYXMgd2VsbC4NCg0KRm9yIGV4YW1wbGUgYGF4aXMudGl0bGVgIGlzIHRoZSBwYXJlbnQgb2YgYGF4aXMudGl0bGUueGAuIEJ5IHNldHRpbmcgdGhlIGBpbmhlcml0LmJsYW5rID0gVFJVRWAgcGFyYW1ldGVyLCB5b3UgY2FuIG92ZXJyaWRlL251bGxpZnkgYWVzdGhldGljcyBhc3NpZ25tZW50IGxheWVycyBhcyBsb25nIGFzIGEgcGFyZW50IGxheWVycyBoYXMgc2V0IHRob3NlIGVsZW1lbnRzIHRvIGBlbGVtZW50LmJsYW5rKClgLiBJdCdzIGEgZ29vZCB3YXkgdG8gcmVtb3ZlIGFkZGl0aW9uYWwgbGF5ZXIgZWZmZWN0cyBpZiBuZWVkZWQhDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgMS4xLjEgTW92ZSB5b3VyIGxlZ2VuZChzKSB1c2luZyB0aGUgYGxlZ2VuZC5wb3NpdGlvbmAgb3B0aW9uDQoNCkxldCdzIHN0YXJ0IHdpdGggb25lIG9mIHRoZSBtb3N0IG9mdC1pbnRydXNpdmUgY29tcG9uZW50cyBvZiBvdXIgdmlzdWFsaXphdGlvbnMuIFdoaWxlIG5lY2Vzc2FyeSwgdGhlIGxlZ2VuZHMgb2Z0ZW4gZGVmYXVsdCB0byB0aGUgcmlnaHQtaGFuZCBzaWRlIG9mIG91ciB2aXN1YWxpemF0aW9ucyB3aGVyZSB0aGV5IGNhbiB0YWtlIHVwIGV4dHJhIGhvcml6b250YWwgc3BhY2Ugd2l0aG91dCByZXF1aXJpbmcgbXVjaCB2ZXJ0aWNhbCBzcGFjZSENCg0KIyMjIDEuMS4xLjEgTW92aW5nIHlvdXIgbGVnZW5kIHdpdGhpbiB0aGUgcGxvdCBhcmVhDQoNCldoZW4gd2UgYXJlIGxvb2tpbmcgdG8gbW92ZSBvdXIgbGVnZW5kcyB0byBkaWZmZXJlbnQgcG9zaXRpb25zLCB0aGVyZSBhcmUgMiBhcmVhcyB0byBjb25zaWRlci4gVGhlIGZpcnN0IGlzIHRoZSBwbG90IGFyZWEgaXRzZWxmIHdoaWNoICoqKnN1cnJvdW5kcyoqKiB0aGUgZGF0YSBwYW5lbCAod2hlcmUgb3VyIGRhdGEgaXMgcGxvdHRlZCkuIFRoZSBgbGVnZW5kLnBvc2l0aW9uYCBwYXJhbWV0ZXIgY2FuIHRha2UgaW4gdHdvIHR5cGVzIG9mIHZhbHVlcy4gVGhlIGZpcnN0IGlzIGEgc2V0IG9mIGNoYXJhY3RlcnM6IGB0b3BgLCBgYm90dG9tYCwgYGxlZnRgLCBhbmQgYHJpZ2h0YCB3aGljaCByZWxhdGVzIHRvIHRoZSBwbG90IGFyZWEuDQoNCkxldCdzIHN0YXJ0IHdpdGggYWx0ZXJpbmcgb3VyIGxlZ2VuZCBwb3NpdGlvbiB3aXRoaW4gdGhlIHBsb3QgYXJlYS4gSXQncyB0YWtpbmcgdXAgcXVpdGUgYSBiaXQgb2Ygc3BhY2Ugb24gdGhlIHNpZGUuIFdlJ2xsIHdvcnJ5IGFib3V0IHRoZSBsYWJlbCBpc3N1ZXMgbGF0ZXIuIEZvciBub3csIGxldCdzIG1vdmUgdGhlIGxlZ2VuZCB0byB0aGUgYm90dG9tIG9mIHRoZSBwbG90LiBBdCB0aGUgc2FtZSB0aW1lLCBsZXQncyBpbmNyZWFzZSBvdXIgb3ZlcmFsbCB0ZXh0IHNpemUgZm9yIHRoZSBwbG90Lg0KDQpgYGB7ciwgZmlnLndpZHRoPTIwLCBmaWcuaGVpZ2h0PTEwfQ0KDQojIEJ1aWxkIG91ciBwbG90IGFuZCBkYXRhIGZyb20gc2NyYXRjaA0KY292aWRfcGh1X3dpbmRvdy5kZiAlPiUgDQogICMgRmlsdGVyIGZvciB0aGUgdG9wIDQgaW5mZWN0ZWQgUEhVcw0KICBmaWx0ZXIocHVibGljX2hlYWx0aF91bml0ICVpbiUgcGh1X2J5X3RvdGFsX2Nhc2VzX2Rlc2NbMTo0XSwNCiAgICAgICAgIHN0YXJ0X2RhdGUgPj0gYXMuRGF0ZSgiMjAyMC0xMi0wMSIpKSAlPiUgDQogIA0KICAjIHJlZGlyZWN0IHRoZSBmaWx0ZXJlZCByZXN1bHQgdG8gZ2dwbG90DQogICMgMS4gRGF0YQ0KICBnZ3Bsb3QoLikgKw0KICAgICMgMi4gQWVzdGhldGljcw0KICAgIGFlcyh4ID0gc3RhcnRfZGF0ZSwgeSA9IHdpbmRvd19tZWFuLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBTZXQgb3VyIHggYW5kIHkgYXhlcw0KICAgICAgICBjb2xvdXIgPSBmY3RfcmVvcmRlcihwdWJsaWNfaGVhbHRoX3VuaXQsIHdpbmRvd19tZWFuLCAuZGVzYz1UUlVFKSkgKyAgIyBSZW9yZGVyIG91ciBQSFVzDQoNCiAgICAjIFRoZW1lIGVsZW1lbnRzDQogICAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTIwKSwgIyBzZXQgdGV4dCBzaXplIHRvIDIwDQogICAgICAgICAgIyMjIDEuMS4xIE1vdmUgdGhlIGxlZ2VuZCB0byB0aGUgYm90dG9tDQogICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSINCiAgICAgICAgICkgKyANCg0KICAgICMgNC4gR2VvbXMNCiAgICBnZW9tX2xpbmUobGluZXdpZHRoPTEpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAxLjEuMS4yIE1vdmUgYSBsZWdlbmQgdG8gd2l0aGluIHlvdXIgZGF0YSBwYW5lbA0KDQpJbnN0ZWFkIG9mIG1vdmluZyB0aGUgbGVnZW5kIHRvIHRoZSBib3R0b20gb2Ygb3VyIHBsb3QgYXJlYSwgbGV0J3MgdXNlIHRoZSBlbXB0eSBzcGFjZSBpbiB0aGUgdG9wIGxlZnQgY29ybmVyIG9mIHRoZSBkYXRhIHBhbmVsIGluc3RlYWQgYnkgYWNjZXNzaW5nIHRoZSBjb29yZGluYXRlIHN5c3RlbSAoMDoxLCAwOjEpIHRoYXQgcmVwcmVzZW50cyB0aGUgcmVsYXRpdmUgcG9zaXRpb25pbmcgb2YgZWxlbWVudHMgd2l0aGluIHRoZSBwYW5lbC4gVGhpcyBzeXN0ZW0sIGZvbGxvd3MgYSBgYyh4LCB5KWAgc2V0dXAgdGhhdCBtYXRjaGVzIHRoZSBkYXRhIHBhbmVsIHdpdGggKDAsMCkgcmVwcmVzZW50aW5nIHRoZSBsb3dlciBsZWZ0IGNvcm5lci4NCg0KQmVmb3JlIHdlIG1vdmUgdGhlIGxlZ2VuZCBvbnRvIG91ciBwYW5lbCwgaG93ZXZlciwgd2UgYWxzbyBoYXZlIHRvIHJlbWVtYmVyICp3aGVyZSogdGhlIGxlZ2VuZCBpdHNlbGYgaXMgYW5jaG9yaW5nIHdoZW4gd2UgbW92ZSBpdC4gQXJlIHdlIGFza2luZyB0byBwdXQgdGhlIGJvdHRvbS1yaWdodCBjb3JuZXIgb2YgdGhlIGxlZ2VuZCBpbnRvIHRoZSB0b3AtbGVmdCBjb3JuZXIgb2YgdGhlIHBsb3Q/IE9yIGRvIHdlIHdhbnQgdG8gbWF0Y2ggdGhlIGxlZ2VuZCBhbmNob3Igc28gdGhhdCB0aGUgdG9wLWxlZnQgY29ybmVycyBhcmUgYWxpZ25lZD8NCg0KVXNlIHRoZSBgbGVnZW5kLmp1c3RpZmljYXRpb25gIHBhcmFtZXRlciB0byBwcm9wZXJseSBzZXQgdGhpcyBwcm9wZXJ0eSB3aGVuIG1vdmluZyB5b3VyIGxlZ2VuZC4gSXQgdXNlcyB0aGUgc2FtZSB0d28tcG9pbnQgY29vcmRpbmF0ZSBjb25jZXB0IHRoYXQgd2UnbGwgdXNlIGZvciBgbGVnZW5kLnBvc2l0aW9uYC4NCg0KYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD0xMH0NCg0KIyBCdWlsZCBvdXIgcGxvdCBhbmQgZGF0YSBmcm9tIHNjcmF0Y2gNCmNvdmlkX3BodV93aW5kb3cuZGYgJT4lIA0KICAjIEZpbHRlciBmb3IgdGhlIHRvcCA0IGluZmVjdGVkIFBIVXMNCiAgZmlsdGVyKHB1YmxpY19oZWFsdGhfdW5pdCAlaW4lIHBodV9ieV90b3RhbF9jYXNlc19kZXNjWzE6NF0sDQogICAgICAgICBzdGFydF9kYXRlID49IGFzLkRhdGUoIjIwMjAtMTItMDEiKSkgJT4lIA0KICANCiAgIyByZWRpcmVjdCB0aGUgZmlsdGVyZWQgcmVzdWx0IHRvIGdncGxvdA0KICAjIDEuIERhdGENCiAgZ2dwbG90KC4pICsNCiAgICAjIDIuIEFlc3RoZXRpY3MNCiAgICBhZXMoeCA9IHN0YXJ0X2RhdGUsIHkgPSB3aW5kb3dfbWVhbiwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgU2V0IG91ciB4IGFuZCB5IGF4ZXMNCiAgICAgICAgY29sb3VyID0gZmN0X3Jlb3JkZXIocHVibGljX2hlYWx0aF91bml0LCB3aW5kb3dfbWVhbiwgLmRlc2M9VFJVRSkpICsgICMgUmVvcmRlciBvdXIgUEhVcw0KDQogICAgIyBUaGVtZSBlbGVtZW50cw0KICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0yMCksICMgc2V0IHRleHQgc2l6ZSB0byAyMA0KICAgICAgICAgICMjIyAxLjEuMS4yIE1vdmUgdGhlIGxlZ2VuZCBhcm91bmQgdG8gd2l0aGluIHRoZSBwYW5lbCBzcGFjZQ0KICAgICAgICAgIGxlZ2VuZC5qdXN0aWZpY2F0aW9uID0gYygwLCAxKSwgICAgICAgICAgIyBTZXQgdGhlIHBvaW50IG9uIHRoZSBsZWdlbmQgeW91IGFyZSBtb3ZpbmcNCiAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSBjKDAuMDIsIDAuOTUpLCAgICAgICAgICMgU2V0IHRoZSBwb2ludCB5b3UgYXJlIG1vdmluZyB0bw0KICAgICAgICAgIGxlZ2VuZC5kaXJlY3Rpb24gPSAiaG9yaXpvbnRhbCINCiAgICAgICAgICkgKyANCg0KICAgICMgNC4gR2VvbXMNCiAgICBnZW9tX2xpbmUobGluZXdpZHRoPTEpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDEuMS4yIFVwZGF0ZSB0aGUgYmFja2dyb3VuZCBwYW5lbCBhbmQgbGluZXMNCg0KVGhlcmUgYXJlIGEgZmV3IG1vcmUgdGhpbmdzIHdlIGNhbiBkbyB0byB0aGUgcGxvdCBmb3Igbm93IHRoYXQgaW5jbHVkZSB1cGRhdGluZyB0aGUgYmFja2dyb3VuZCBwYW5lbCB0byBnZXQgcmlkIG9mIHRoZSBncmV5IGNvbG91ciBhbmQgbWF5YmUgZGFya2VuaW5nIG91ciBheGlzIHRpY2sgbGluZXMgYW5kIGF4aXMgbGluZXMgdGhlbXNlbHZlcy4NCg0KMS4gIFdlJ2xsIHVzZSB0aGUgYHBhbmVsLmJhY2tncm91bmRgIHBhcmFtZXRlciB3aGljaCBleHBlY3RzIGFuIGBlbGVtZW50X3JlY3QoKWAgdG8gZGVmaW5lIGl0J3MgcHJvcGVydGllcy4NCjIuICBgcGFuZWwuZ3JpZC4qYCBnaXZlcyB1cyBhY2Nlc3MgdG8gdGhlIGJhY2tncm91bmQgYXhlcyBsaW5lcyB1c2luZyBgZWxlbWVudF9saW5lKClgDQozLiAgV2UnbGwgd29yayB3aXRoIGBheGlzLipgIGVsZW1lbnRzIHRvIHRvIHVwZGF0ZSB0aGVpciBmb3JtYXQgYSBiaXQgdG9vLg0KNC4gIExldCdzIHNwaWNlIHVwIHRoZSBgcGxvdGAgYSBsaXR0bGUgYml0IGJ5IHNldHRpbmcgdGhlIG92ZXJhbGwgYmFja2dyb3VuZCBjb2xvdXIuDQoNCmBgYHtyLCBmaWcud2lkdGg9MjAsIGZpZy5oZWlnaHQ9MTB9DQoNCiMgQnVpbGQgb3VyIHBsb3QgYW5kIGRhdGEgZnJvbSBzY3JhdGNoDQpjb3ZpZF9waHVfd2luZG93LmRmICU+JSANCiAgIyBGaWx0ZXIgZm9yIHRoZSB0b3AgNSBpbmZlY3RlZCBQSFVzDQogIGZpbHRlcihwdWJsaWNfaGVhbHRoX3VuaXQgJWluJSBwaHVfYnlfdG90YWxfY2FzZXNfZGVzY1sxOjRdKSAlPiUgDQogIA0KICAjIHJlZGlyZWN0IHRoZSBmaWx0ZXJlZCByZXN1bHQgdG8gZ2dwbG90DQogICMgMS4gRGF0YQ0KICBnZ3Bsb3QoLikgKw0KICAgICMgMi4gQWVzdGhldGljcw0KICAgIGFlcyh4ID0gc3RhcnRfZGF0ZSwgeSA9IHdpbmRvd19tZWFuLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBTZXQgb3VyIHggYW5kIHkgYXhlcw0KICAgICAgICBjb2xvdXIgPSBmY3RfcmVvcmRlcihwdWJsaWNfaGVhbHRoX3VuaXQsIHdpbmRvd19tZWFuLCAuZGVzYz1UUlVFKSkgKyAgIyBSZW9yZGVyIG91ciBQSFVzDQoNCiAgICAjIFRoZW1lIGVsZW1lbnRzDQogICAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTIwKSwgIyBzZXQgdGV4dCBzaXplIHRvIDIwDQoNCiAgICAgICAgICAjIE1vdmUgdGhlIGxlZ2VuZCBhcm91bmQgdG8gd2l0aGluIHRoZSBwYW5lbCBzcGFjZQ0KICAgICAgICAgIGxlZ2VuZC5qdXN0aWZpY2F0aW9uID0gYygwLDEpLA0KICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC4wMiwwLjk1KSwNCiAgICAgICAgICBsZWdlbmQuZGlyZWN0aW9uID0gImhvcml6b250YWwiLA0KDQogICAgICAgICAgIyMjIDEuMS4yIFVwZGF0ZSB0aGUgcGFuZWwgY29sb3VyIGFuZCBsaW5lIGNvbG91cnMNCiAgICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KCJ3aGl0ZSIpLA0KICAgICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2xpbmUoImdyZXkiKSwNCg0KICAgICAgICAgICMjIyAxLjEuMiBVc2UgYSBibGFjayBsaW5lIGZvciB0aGUgYXhlcw0KICAgICAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXI9ImJsYWNrIiksDQogICAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KGNvbG91cj0iYmxhY2siLCBmYWNlPSJib2xkIiksDQogICAgICAgICAgDQogICAgICAgICAgIyMjIDEuMS4yIFVwZGF0ZSB0aGUgcGxvdCBiYWNrZ3JvdW5kIGNvbG91cg0KICAgICAgICAgIHBsb3QuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdCgibGlnaHRibHVlIikNCiAgICAgICAgICkgKw0KDQogICAgIyA0LiBHZW9tcw0KICAgIGdlb21fbGluZShsaW5ld2lkdGg9MSkNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KOjo6IHsuYWxlcnQgLmFsZXJ0LWJsb2NrIC5hbGVydC1zdWNjZXNzfQ0KKipPbmUgdnMuIG11bHRpcGxlIHRoZW1lKCkgbGF5ZXJzOioqIFlvdSdsbCBub3RpY2UgZnJvbSBvdXIgY29kZSBhYm92ZSwgdGhhdCB3ZSBvbmx5IG1ha2UgYSAqKipzaW5nbGUqKiogY2FsbCB0byB0aGUgKip0aGVtZSgpKiogbGF5ZXIuIEVhY2ggbGluZSwgaG93ZXZlciwgcmVwcmVzZW50cyBhIGRpZmZlcmVudCBlbGVtZW50IG9mIHRoZSB0aGVtZSB0aGF0IHdlIGFyZSBhbHRlcmluZy4gSW4gZ2VuZXJhbCwgd2hpbGUgdGhlIG9yZGVyIG9mIHRoZXNlIGl0ZW1zIGRvZXMgbWF0dGVyLCBpZiBpdCBtYWtlcyBzZW5zZSBmb3IgeW91LCB5b3UgY2FuIGFkZCBtdWx0aXBsZSBsYXllcnMgZm9yICoqdGhlbWUoKSoqIGdyb3VwaW5nIHRoZW0gYnkgdGhlIHNwZWNpZmljIGVsZW1lbnQgdHlwZXMgeW91IHdhbnQgdG8gd29yayB3aXRoIGxpa2UgYXhlcywgYmFja2dyb3VuZCwgYW5kIHRpdGxlcy4NCjo6Og0KDQojIyAxLjIuMCBVc2UgcHJlbWFkZSB0aGVtZXMgZnJvbSBgZ2dwbG90MmANCg0KSW4gb3VyIGFib3ZlIGV4YW1wbGUgd2UgbWFkZSBhbHRlcmF0aW9ucyB0byB0aGUgdGhlbWUgdGhhdCBhZmZlY3RlZCBiYWNrZ3JvdW5kIGNvbG91ciBhbmQgYXhpcyBsaW5lcy4gV2hpbGUgc29tZSBvZiB5b3UgbWF5IGxlYW4gb24gdGhlIG1vcmUgYXJ0aXN0aWMgc2lkZSB5b3UgY2FuIGFsc28gdXNlIHByZW1hZGUgdGhlbWVzIGZyb20gYm90aCB0aGUgYGdncGxvdDJgIHBhY2thZ2UgYW5kIGFkZGl0aW9uYWwgcGFja2FnZXMgbGlrZSBgZ2d0aGVtZXNgLiBCZWxvdyB5b3UnbGwgZmluZCBhIGxpc3Qgb2YgdGhlIHRoZW1lcyBmcm9tIGBnZ3Bsb3QyYC4NCg0KfCBUaGVtZSAgICAgICAgICAgIHwgRGVzY3JpcHRpb24gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnw6LS0tLS0tLS0tLS0tLS0tLS0tLS0tLXw6LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18DQp8IHRoZW1lX2dyYXkoKSAgICAgfCBHcmV5IGJhY2tncm91bmQgY29sb3VyLCB3aGl0ZSBncmlkIGxpbmVzLiAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCB0aGVtZV9idygpICAgICAgIHwgV2hpdGUgYmFja2dyb3VuZCBjb2xvdXIsIGdyZXkgZ3JpZCBsaW5lcy4gICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgdGhlbWVfbGluZWRyYXcoKSB8IFdoaXRlIGJhY2tncm91bmQgY29sb3VyLCBibGFjayBsaW5lcyBvZiB2YXJpb3VzIHdpZHRocyAgICAgICAgICAgICB8DQp8IHRoZW1lX2xpZ2h0KCkgICAgfCBXaGl0ZSBiYWNrZ3JvdW5kIGNvbG91ciwgZ3JleSBsaW5lcyBvZiB2YXJpb3VzIHdpZHRocyAgICAgICAgICAgICAgfA0KfCB0aGVtZV9kYXJrKCkgICAgIHwgRGFyayBiYWNrZ3JvdW5kIGNvbG91ciwgZ3JleSBsaW5lcyBvZiB2YXJpb3VzIHdpZHRocyAgICAgICAgICAgICAgIHwNCnwgdGhlbWVfbWluaW1hbCgpICB8IE5vIGJhY2tncm91bmQgYW5ub3RhdGlvbnMsIGdyZXkgbGluZXMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IHRoZW1lX2NsYXNzaWMoKSAgfCBXaGl0ZSBiYWNrZ3JvdW5kLCB4L3kgYXhpcyBsaW5lcywgbm8gZ3JpZCBsaW5lcyAgICAgICAgICAgICAgICAgICAgfA0KfCB0aGVtZV92b2lkKCkgICAgIHwgQSBjb21wbGV0ZWx5IGVtcHR5IHRoZW1lcywgd2hpdGUgYmFja2dyb3VuZCwgbm8gYXhpcyBvciBncmlkIGxpbmVzIHwNCg0KSWYgeW91IGZpbmQgYSB0aGVtZSB0aGF0IHlvdSAqKiptb3N0bHkqKiogbGlrZSwgeW91IGNhbiB1c2UgdGhhdCBhcyBhIGJhc2UgdG8geW91ciBncmFwaCAqKipiZWZvcmUqKiogbWFraW5nIGFkZGl0aW9uYWwgYHRoZW1lKClgIGFsdGVyYXRpb25zLiBMZXQncyB0cnkgYSBmZXcgb2YgdGhlc2Ugb3V0Lg0KDQpgYGB7ciwgZmlnLndpZHRoPTIwLCBmaWcuaGVpZ2h0PTEwfQ0KDQojIEJ1aWxkIG91ciBwbG90IGFuZCBzYXZlIHRvIGFuIG9iamVjdA0KcGh1X3dpbmRvdy5wbG90IDwtIGNvdmlkX3BodV93aW5kb3cuZGYgJT4lIA0KICAjIEZpbHRlciBmb3IgdGhlIHRvcCA1IGluZmVjdGVkIFBIVXMNCiAgZmlsdGVyKHB1YmxpY19oZWFsdGhfdW5pdCAlaW4lIHBodV9ieV90b3RhbF9jYXNlc19kZXNjWzE6NF0pICU+JSANCiAgDQogICMgcmVkaXJlY3QgdGhlIGZpbHRlcmVkIHJlc3VsdCB0byBnZ3Bsb3QNCiAgIyAxLiBEYXRhDQogIGdncGxvdCguKSArDQogICAgIyAyLiBBZXN0aGV0aWNzDQogICAgYWVzKHggPSBzdGFydF9kYXRlLCB5ID0gd2luZG93X21lYW4sICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIFNldCBvdXIgeCBhbmQgeSBheGVzDQogICAgICAgIGNvbG91ciA9IGZjdF9yZW9yZGVyKHB1YmxpY19oZWFsdGhfdW5pdCwgd2luZG93X21lYW4sIC5kZXNjPVRSVUUpKSArICAjIFJlb3JkZXIgb3VyIFBIVXMNCg0KICAgICMgVGhlbWUgZWxlbWVudHMNCiAgICAjIyMgMS4yLjAgU3RhcnQgd2l0aCBhIGJhc2UgdGhlbWUNCiAgICB0aGVtZV9taW5pbWFsKCkgKw0KDQogICAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTIwKSwgIyBzZXQgdGV4dCBzaXplIHRvIDIwDQoNCiAgICAgICAgICAjIE1vdmUgdGhlIGxlZ2VuZCBhcm91bmQgdG8gd2l0aGluIHRoZSBwYW5lbCBzcGFjZQ0KICAgICAgICAgIGxlZ2VuZC5qdXN0aWZpY2F0aW9uID0gYygwLDEpLA0KICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC4wMiwwLjk1KSwNCiAgICAgICAgICBsZWdlbmQuZGlyZWN0aW9uID0gImhvcml6b250YWwiLCANCg0KICAgICAgICAgICMgVXBkYXRlIHRoZSBwYW5lbCB0byBkcm9wIHRoZSBtaW5vciB5LWF4aXMgZ3JpZCBsaW5lcw0KICAgICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksDQoNCiAgICAgICAgICAjIFVzZSBhIGJsYWNrIGxpbmUgZm9yIHRoZSBheGVzDQogICAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91cj0iYmxhY2siKSwNCiAgICAgICAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoY29sb3VyPSJibGFjayIsIGZhY2U9ImJvbGQiKSwNCiAgICAgICAgICkgKw0KDQogICAgIyA0LiBHZW9tcw0KICAgIGdlb21fbGluZShzaXplPTEpDQoNCiMgcGxvdCBvdXIgb2JqZWN0IHRvIHN0YW5kYXJkIG91dHB1dA0KcGh1X3dpbmRvdy5wbG90DQpgYGANCg0KYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD0xMH0NCiMgVHJ5IHRvIGFkZCB0aGVtZV9kYXJrKCkgdG8gb3VyIHBsb3QuIFdoYXQgYXJlIHRoZSBjb25zZXF1ZW5jZXM/DQpwaHVfd2luZG93LnBsb3QgKyB0aGVtZV9kYXJrKCkNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KOjo6IHsuYWxlcnQgLmFsZXJ0LWJsb2NrIC5hbGVydC13YXJuaW5nfQ0KKipMYXllciBvcmRlciBtYXR0ZXJzISoqIEl0IGNhbm5vdCBiZSBzdHJlc3NlZCBlbm91Z2ggdGhhdCBsYXllciBvcmRlciBtYXR0ZXJzLiBXZSd2ZSBtZW50aW9uZWQgaXQgaW4gcHJldmlvdXMgc2VjdGlvbnMgYXMgd2Ugd29yayB0aHJvdWdoIHRoZXNlIGZpZ3VyZXMgYnV0IHRoZSBhYm92ZSBjb2RlIGlzIG91ciBjbGVhcmVzdCBleGFtcGxlLiBFdmVuIHRob3VnaCB3ZSBoYWQgc2V0IHRoZSBmb250IGZvcm1hdHMsIGFuZCBsZWdlbmQgcG9zaXRpb25zLCBhbGwgb2YgdGhhdCB3YXMgZXJhc2VkIHdpdGggYSBzaW5nbGUgYWRkZWQgKip0aGVtZV9kYXJrKCkqKiBsYXllci4gVGhpcyBpcyBiZWNhdXNlIHRoZSAqKiptb3N0IHJlY2VudCBsYXllcioqKiBvdmVycmlkZXMgYWxsIG9mIHRoZSBhZXN0aGV0aWNzIGZyb20gcHJldmlvdXMgb25lcy4gU29tZXRpbWVzIHRoaXMgaGFzIG9ubHkgYSBzbWFsbCBlZmZlY3QgZGVwZW5kaW5nIG9uIHRoZSBpbmhlcml0YW5jZSBzdHJ1Y3R1cmUgb3IgaXQgY2FuIGVzc2VudGlhbGx5IHJlc2V0IGV2ZXJ5dGhpbmchICpDYXZlYXQgZW1wdG9yISoNCjo6Og0KDQojIyAxLjMuMCBgZ2d0aGVtZXNgIG1pbWljcyB2aXN1YWwgc3R5bGVzIGZyb20gbXVsdGlwbGUgc291cmNlcw0KDQpJZiB5b3UgYXJlIGZlZWxpbmcgYSBsaXR0bGUgbW9yZSBkYXJpbmcgd2l0aCB5b3VyIGNob2ljZXMsIHlvdSBjYW4gdHVybiB0byB0aGUgYGdndGhlbWVzYCBwYWNrYWdlcyB0byBtaW1pYyBzdHlsZXMgZnJvbSBhIG51bWJlciBvZiBwdWJsaWNhdGlvbnMgc3VjaCBhcyB0aGUgRWNvbm9taXN0LCBhbmQgV2FsbCBTdHJlZXQgSm91cm5hbC4gWW91IGNhbiBsb29rIHVwIGEgbGlzdCBvZiB0aGUgdmFyaW91cyB0aGVtZXMgYXQgPGh0dHBzOi8vZ2l0aHViLmNvbS9qcm5vbGQvZ2d0aGVtZXM+Lg0KDQpMaWtlIHRoZSB0aGVtZXMgcHJvdmlkZWQgYnkgZ2dwbG90LCB5b3UgY2FuIGFsc28gbWFrZSBlZGl0cyB0byB0aGVzZSB0aGVtZXMgd2l0aGluIHlvdXIgc2NyaXB0cy4NCg0KVHdvIGFkZGl0aW9uYWwgcGFja2FnZSBvcHRpb25zIHdpdGggZGlmZmVyZW50IGNvbG91ciBwYWxldHRlcyBhbmQgc2hhcGVzIGFyZSBgZ2d0aGVtcmAgYW5kIGBnZ3NjaWAuDQoNCmBgYHtyLCBmaWcud2lkdGg9MjAsIGZpZy5oZWlnaHQ9MTB9DQoNCiMgQnVpbGQgb3VyIHBsb3QNCmNvdmlkX3BodV93aW5kb3cuZGYgJT4lIA0KICAjIEZpbHRlciBmb3IgdGhlIHRvcCA1IGluZmVjdGVkIFBIVXMNCiAgZmlsdGVyKHB1YmxpY19oZWFsdGhfdW5pdCAlaW4lIHBodV9ieV90b3RhbF9jYXNlc19kZXNjWzE6NF0pICU+JSANCiAgDQogICMgcmVkaXJlY3QgdGhlIGZpbHRlcmVkIHJlc3VsdCB0byBnZ3Bsb3QNCiAgIyAxLiBEYXRhDQogIGdncGxvdCguKSArDQogICAgIyAyLiBBZXN0aGV0aWNzDQogICAgYWVzKHggPSBzdGFydF9kYXRlLCB5ID0gd2luZG93X21lYW4sICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIFNldCBvdXIgeCBhbmQgeSBheGVzDQogICAgICAgIGNvbG91ciA9IGZjdF9yZW9yZGVyKHB1YmxpY19oZWFsdGhfdW5pdCwgd2luZG93X21lYW4sIC5kZXNjPVRSVUUpKSArICAjIFJlb3JkZXIgb3VyIFBIVXMNCg0KICAgICMgVGhlbWUgZWxlbWVudHMNCiAgICAjIyMgMS4zLjAgU3dpdGNoIHRvIHRoZSBzdGF0YSB0aGVtZQ0KICAgIHRoZW1lX3N0YXRhKCkgKw0KDQogICAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTIwKSwgIyBzZXQgdGV4dCBzaXplIHRvIDIwDQogICAgICAgICAgIyBNb3ZlIHRoZSBsZWdlbmQgYXJvdW5kIHRvIHdpdGhpbiB0aGUgcGFuZWwgc3BhY2UNCiAgICAgICAgICBsZWdlbmQuanVzdGlmaWNhdGlvbiA9IGMoMCwxKSwNCiAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSBjKDAuMDIsMC45NSksDQogICAgICAgICAgbGVnZW5kLmRpcmVjdGlvbiA9ICJob3Jpem9udGFsIiwgDQogICAgICAgICAgDQogICAgICAgICAgIyBVc2UgYSBibGFjayBsaW5lIGZvciB0aGUgYXhlcw0KICAgICAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siKSwNCiAgICAgICAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoY29sb3VyID0gImJsYWNrIiwgZmFjZT0iYm9sZCIpLCAgICAgICAgICANCiAgICAgICAgICkgKw0KDQogICAgIyA0LiBHZW9tcw0KICAgIGdlb21fbGluZShzaXplPTEpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMgMi4wLjAgVGV4dCBjb250ZW50IGNhbiBiZSB1cGRhdGVkIHRocm91Z2ggYSBudW1iZXIgb2YgbGF5ZXJzDQoNCk5vdyB0aGF0IHdlIGhhdmUgcGxheWVkIGFyb3VuZCB3aXRoIGhvdyB0byByZXBvc2l0aW9uIGxlZ2VuZHMsIGFuZCBvdGhlciBlbGVtZW50cyBvZiB5b3VyIHBsb3QsIHdlIGNhbiBkaXNjdXNzIGhvdyB0byBjaGFuZ2UgdGhlICphY3R1YWwqIHRleHQgY29udGVudCBvZiB5b3VyIHBsb3QuIE1hbnkgdGltZXMgd2Ugd2FudCB0byByZWxhYmVsIGF4ZXMgb3IgbGVnZW5kcywgZXZlbiBsZWdlbmQgbGFiZWxzLiBUaGVyZSBhcmUgYSBudW1iZXIgb2YgbGF5ZXJzIHdlIGNhbiB3b3JrIHRocm91Z2ggYnV0IHdlJ2xsIHByZXNlbnQgc29tZSBvZiB0aGUgc2ltcGxlc3Qgd2F5cyB0byBhY2NvbXBsaXNoIHRoaXMuDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyAyLjEuMCBMYWJlbCB0aXRsZXMgYW5kIGF4ZXMgaW5kaXZpZHVhbGx5IG9yIHdpdGggdGhlIGBsYWJzKClgIGNvbW1hbmQNCg0KVXAgdG8gdGhpcyBwb2ludCwgd2UndmUgc2VlbiB0aGUgdXNlIG9mIGRpZmZlcmVudCBjb21tYW5kcyB0byBhbHRlciB0aGUgbGFiZWxzIGFuZCB0aXRsZXMgbGlrZToNCg0KLSAgIGB4bGFiKClgOiBVcGRhdGUgdGhlIHgtYXhpcyBsYWJlbC4NCg0KLSAgIGB5bGFiKClgOiBVcGRhdGUgdGhlIHktYXhpcyBsYWJlbC4NCg0KLSAgIGBnZ3RpdGxlKClgOiBVcGRhdGUgdGhlIHBsb3QgdGl0bGUuDQoNCllvdSBjYW4gYWxzbyBhY2Nlc3MgbXVsdGlwbGUgb3B0aW9ucyB3aXRoaW4gYSBzaW5nbGUgY2FsbCB0byB0aGUgYGxhYnMoKWAgbGF5ZXIgd2hpY2ggYWNjZXB0cyB0aGUgZm9sbG93aW5nIHBhcmFtZXRlcnM6DQoNCi0gICBgLi4uYDogYSBsaXN0IG9mIG5hbWUtdmFsdWUgcGFpcnMgdGhhdCBtYXAgYmFjayB0byBhbiBhZXN0aGV0aWMgKGllIGB4ID0gIlgtYXhpcyJgIG9yIGBjb2xvdXIgPSAiUG9wdWxhdGlvbiJgKQ0KDQotICAgVXNlIHRoZSBgTlVMTGAgdmFsdWUgdG8gcmVtb3ZlIGEgdGl0bGUgZm9yIGEgc3BlY2lmaWMgbGFiZWwuDQoNCi0gICBgdGl0bGVgLCBgc3VidGl0bGVgOiB0aGUgdGl0bGUgd2l0aCBhIHN1YnRpdGxlIGRpc3BsYXllZCBiZWxvdw0KDQotICAgYGNhcHRpb25gOiB0aGUgdGV4dCBmb3IgdGhlIGNhcHRpb24gaXMgZGlzcGxheWVkIGluIHRoZSBib3R0b20tcmlnaHQgYnkgZGVmYXVsdA0KDQotICAgYHRhZ2A6IGZpZ3VyZSB0ZXh0IHRhZy9sYWJlbCB1c3VhbGx5IGZvciBmaWd1cmUgcGFuZWxzIGluIG1hbnVzY3JpcHRzDQoNCkxldCdzIHJlbGFiZWwgb3VyIHBsb3QgYXhpcyBhbmQgdGl0bGVzIHRvIGJlIG1vcmUgYWNjdXJhdGUuIEZvciBub3cgd2UnbGwgZHJvcCB0aGUgKipTdGF0YSoqIHRoZW1lIGFuZCBnbyB3aXRoIG91ciBvd24gYWx0ZXJhdGlvbiBvZiBgdGhlbWVfbWluaW1hbCgpYC4gV2UnbGwgYWxzbyBpbmNsdWRlIGEgY2FwdGlvbiBpbiB0aGUgYm90dG9tIHJpZ2h0IHRvIGV4cGxhaW4gaG93IHdlIGRpc3BsYXkgdGhlIDE0LWRheSByb2xsaW5nIG1lYW4uIFlvdSdsbCBhbHNvIG5vdGljZSB0aGF0IHRoZSBleHRyZW1lbHkgbG9uZyBsZWdlbmQgdGl0bGUgd2lsbCBiZSBxdWl0ZSBlYXNpbHkgZml4ZWQhDQoNCioqTm90ZSoqOiBhIHF1aWNrIHdheSBvZiBhZGRpbmcgc3BhY2UgdG8geW91ciB0aXRsZXMsIGlzIHRvIGluY2x1ZGUgdGhlIGBcbmAgY2hhcmFjdGVyIHdoaWNoIGluc2VydHMgYSBjYXJyaWFnZSByZXR1cm4uDQoNCmBgYHtyLCBmaWcud2lkdGg9MjAsIGZpZy5oZWlnaHQ9MTB9DQoNCiMgQnVpbGQgb3VyIHBsb3QNCmNvdmlkX3BodV93aW5kb3cuZGYgJT4lIA0KICAjIEZpbHRlciBmb3IgdGhlIHRvcCA1IGluZmVjdGVkIFBIVXMNCiAgZmlsdGVyKHB1YmxpY19oZWFsdGhfdW5pdCAlaW4lIHBodV9ieV90b3RhbF9jYXNlc19kZXNjWzE6NF0pICU+JSANCiAgDQogICMgcmVkaXJlY3QgdGhlIGZpbHRlcmVkIHJlc3VsdCB0byBnZ3Bsb3QNCiAgIyAxLiBEYXRhDQogIGdncGxvdCguKSArDQogICAgIyAyLiBBZXN0aGV0aWNzDQogICAgYWVzKHggPSBzdGFydF9kYXRlLCB5ID0gd2luZG93X21lYW4sICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIFNldCBvdXIgeCBhbmQgeSBheGVzDQogICAgICAgIGNvbG91ciA9IGZjdF9yZW9yZGVyKHB1YmxpY19oZWFsdGhfdW5pdCwgd2luZG93X21lYW4sIC5kZXNjPVRSVUUpKSArICAjIFJlb3JkZXIgb3VyIFBIVXMNCg0KICAgICMgVGhlbWUgZWxlbWVudHMNCiAgICAjIFN0YXJ0IHdpdGggYSBiYXNlIHRoZW1lDQogICAgdGhlbWVfbWluaW1hbCgpICsNCg0KICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0yMCksICMgc2V0IHRleHQgc2l6ZSB0byAyMA0KDQogICAgICAgICAgIyBNb3ZlIHRoZSBsZWdlbmQgYXJvdW5kIHRvIHdpdGhpbiB0aGUgcGFuZWwgc3BhY2UNCiAgICAgICAgICBsZWdlbmQuanVzdGlmaWNhdGlvbiA9IGMoMCwxKSwNCiAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSBjKDAuMDIsMC45NSksDQogICAgICAgICAgbGVnZW5kLmRpcmVjdGlvbiA9ICJob3Jpem9udGFsIiwgDQoNCiAgICAgICAgICAjIFVwZGF0ZSB0aGUgcGFuZWwgdG8gZHJvcCB0aGUgbWlub3IgYXhpcyBncmlkIGxpbmVzDQogICAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwgICAgICAgIA0KDQogICAgICAgICAgIyBVc2UgYSBibGFjayBsaW5lIGZvciB0aGUgYXhlcw0KICAgICAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siKSwNCiAgICAgICAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoY29sb3VyID0gImJsYWNrIiwgZmFjZT0iYm9sZCIpLA0KICAgICAgICAgKSArDQoNCiAgICAjIyMgMi4xLjAgQWRkIGxhYmVscyB0byBvdXIgcGxvdA0KICAgIGxhYnModGl0bGUgPSAiTWVhbiBjYXNlcyBvZiBDT1ZJRC0xOSBpbiBhIDE0LWRheSB3aW5kb3cgYWNyb3NzIHRvcCA0IE9udGFyaW8gUHVibGljIEhlYWx0aCBVbml0c1xuIiwNCiAgICAgICAgIHggPSAiXG5XaW5kb3cgZGF0ZSIsDQogICAgICAgICB5ID0gIk1lYW4gY2FzZXMgaW4gMTQtZGF5IHdpbmRvd1xuIiwNCiAgICAgICAgIGNvbG91ciA9ICJQdWJsaWMgSGVhbHRoIFVuaXQiLA0KICAgICAgICAgY2FwdGlvbiA9ICIqMTQtZGF5IHJvbGxpbmcgbWVhbiB3aXRoIGRhdGUgYXMgc3RhcnQgb2YgdGhlIHdpbmRvdyIpICsNCg0KICAgICMgNC4gR2VvbXMNCiAgICBnZW9tX2xpbmUobGluZXdpZHRoPTEpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDIuMi4wIFJlbGFiZWwgYXhpcyB0aWNrcywgYW5kIGxlZ2VuZCBsYWJlbHMgd2l0aCB0aGUgYGxhYmVsc2AgcGFyYW1ldGVyDQoNCkluIGxhc3QgbGVjdHVyZSdzIGFzc2lnbm1lbnQsIHlvdSBsaWtlbHkgd291bGQgaGF2ZSB1c2VkIHRoZSBgeGxpbSgpYCBvciBgeWxpbSgpYCBsYXllcnMgdG8gc2V0IHRoZSBheGlzIGxpbWl0cyBvbiBzb21lIG9mIHlvdXIgdmlzdWFsaXphdGlvbnMuIEFzIHdpdGggYWxsIHRoaW5ncywgdGhlcmUgaXMgbW9yZSB0aGFuIG9uZSBwYXRod2F5IHRvIG91ciBnb2Fscy4NCg0KVGhlIGBzY2FsZV8qKClgIGZ1bmN0aW9ucyBjYW4gYWxzbyBiZSB1c2VkIHRvIHNldCB0aGUgdGl0bGUsIGxpbWl0cywgYnJlYWtzLCBhbmQgbGFiZWxzIGFsb25nIHlvdXIgYXhlcy4gU29tZSBvZiB0aGVzZSBwYXJhbWV0ZXJzIGFyZSByZWR1bmRhbnQgYW5kIGNhbiBvdmVycmlkZSBvdGhlciBgZ2dwbG90MmAgbGF5ZXIgY29tbWFuZHMsIGRlcGVuZGluZyBvbiB0aGUgb3JkZXIgeW91IGhhdmUgaW5jbHVkZWQgdGhlbS4NCg0KfCBQYXJhbWV0ZXIgfCBFcXVpdmFsZW50IGdncGxvdCBsYXllciBjb21tYW5kICAgICAgICAgICAgICAgICAgICB8DQp8Oi0tLS0tLS0tLS18Oi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwNCnwgbmFtZSAgICAgIHwgeGxhYigpLCB5bGFiKCksIGxhYih4KSwgbGFiKHkpICAgICAgICAgICAgICAgICAgICAgfA0KfCBsaW1pdHMgICAgfCB4bGltKCksIHlsaW0oKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IGJyZWFrICAgICB8IERldGVybWluZSB3aGVuIGF4aXMgdGljayBtYXJrcyBhcmUgZ2VuZXJhdGVkICAgICAgIHwNCnwgbGFiZWxzICAgIHwgKioqUmVuYW1lKioqIHRoZSBsYWJlbHMgcHJlc2VudCBhdCBheGlzIHRpY2sgbWFya3MgfA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDIuMi4xIFJlbGFiZWwgZGF0ZSBheGVzIGFuZCBsZWdlbmQgbGFiZWxzIHdpdGggYHNjYWxlXypfZGF0ZSgpYA0KDQpXZSdsbCBzdGFydCB3aXRoIGEgZmFtaWxpYXIgaWRlYSB3ZSd2ZSBiZWVuIHdvcmtpbmcgd2l0aCBzaW5jZSBsZWN0dXJlIDEuIEEgZ29vZCBwb3J0aW9uIG9mIG91ciBwYW5kZW1pYyB2aXN1YWxpemF0aW9ucyBoYXZlIGZvY3VzZWQgb24gbG9va2luZyBhdCBkYXRhIG92ZXIgdGltZS4gV2l0aCB0aGUgYHNjYWxlX3hfZGF0ZSgpYCBsYXllciwgd2UgaGF2ZSBzZXQgbGltaXRzLCBicmVha3MgYW5kIGxhYmVsIGZvcm1hdHMuIFVubGlrZSBtb3JlIGRpc2NyZXRlIGRhdGEgc2V0cyB0aGF0IHdlJ2xsIHNlZSBsYXRlciwgdGhlIGBzY2FsZV8qX2RhdGUoKWAgbGF5ZXIgaGFzIHNvbWUgdmVyeSBzcGVjaWZpYyBwYXJhbWV0ZXJzIHRoYXQgc3Vycm91bmQgdGhlIGlkZWEgb2YgZGF0ZXMgYW5kIGhvdyB0aGV5IGFyZSBmb3JtYXR0ZWQuIExhc3Qgd2VlayB3ZSB0b29rIGEgY2xvc2UgbG9vayBhdCBgc2NhbGVfeF9kYXRlKClgIGluIHNlY3Rpb24gKiozLjMuMioqIG9mIHRoZSBsZWN0dXJlOg0KDQotICAgYGJyZWFrc2A6IHdoaWxlIHlvdSBjYW4gc2V0IHNwZWNpZmljIGJyZWFrcyBmb3IgZGF0ZXMgd2l0aCB0aGlzIHBhcmFtZXRlciB5b3Ugd2lsbCBuZWVkIGEgc3BlY2lmaWMgdmVjdG9yIG9mIGBkYXRlYCB2YWx1ZXMgdGhhdCBtYXRjaGVzIHlvdXIgb3duIGRhdGEgZ3JvdXBzLg0KDQotICAgYGRhdGVfYnJlYWtzYDogYSBjb252ZW5pZW50IHN0cmluZyByZXByZXNlbnRhdGlvbiB0byBkZXNjcmliZSB0aGUgKmRpc3RhbmNlKiBiZXR3ZWVuIGJyZWFrcyBsaWtlIGAiMTIgZGF5cyJgLCBvciBgIjMgeWVhcnMiYC4gVGhpcyBwYXJhbWV0ZXIgd2lsbCBvdmVycmlkZSBhbnkgaW5mb3JtYXRpb24gcGFzc2VkIHRvIGBicmVha3NgLg0KDQotICAgYGRhdGVfbGFiZWxzYDogYSBjb252ZW5pZW50IHN0cmluZyByZXByZXNlbnRhdGlvbiB0byBkZXNjcmliZSB0aGUgKmZvcm1hdCogb2YgZGF0ZXMgZGVmaW5lZCBieSBgc3RyZnRpbWUoKWAuIEluZm9ybWF0aW9uIGZvdW5kIFtoZXJlXShodHRwczovL3JkcnIuaW8vci9iYXNlL3N0cnB0aW1lLmh0bWwpDQoNCkxldCdzIHN0YXJ0IGJ5IHJlbGFiZWxpbmcgb3VyIHgtYXhpcyB0byBzaG93IHVzIG91ciBkYXRlcyBieSBtb250aCBhbmQgYXQgdGhlIHNhbWUgdGltZSB3ZSBzZXQgYSBsaW1pdCB0byBzaG93IHVzIGRhdGEgc3RhcnRpbmcgaW4gRGVjZW1iZXIgb2YgMjAyMC4gV2UndmUgZG9uZSB0aGlzIGJlZm9yZSBzbyBpdCBzaG91bGQgYmUgZWFzeS4NCg0KYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD0xMH0NCg0KIyBCdWlsZCBvdXIgcGxvdA0KY292aWRfcGh1X3dpbmRvdy5kZiAlPiUgDQogICMgRmlsdGVyIGZvciB0aGUgdG9wIDUgaW5mZWN0ZWQgUEhVcw0KICBmaWx0ZXIocHVibGljX2hlYWx0aF91bml0ICVpbiUgcGh1X2J5X3RvdGFsX2Nhc2VzX2Rlc2NbMTo0XSkgJT4lIA0KICANCiAgIyByZWRpcmVjdCB0aGUgZmlsdGVyZWQgcmVzdWx0IHRvIGdncGxvdA0KICAjIDEuIERhdGENCiAgZ2dwbG90KC4pICsNCiAgICAjIDIuIEFlc3RoZXRpY3MNCiAgICBhZXMoeCA9IHN0YXJ0X2RhdGUsIHkgPSB3aW5kb3dfbWVhbiwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgU2V0IG91ciB4IGFuZCB5IGF4ZXMNCiAgICAgICAgY29sb3VyID0gZmN0X3Jlb3JkZXIocHVibGljX2hlYWx0aF91bml0LCB3aW5kb3dfbWVhbiwgLmRlc2M9VFJVRSkpICsgICMgUmVvcmRlciBvdXIgUEhVcw0KDQogICAgIyBUaGVtZSBlbGVtZW50cw0KICAgICMgU3RhcnQgd2l0aCBhIGJhc2UgdGhlbWUNCiAgICB0aGVtZV9taW5pbWFsKCkgKw0KDQogICAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTIwKSwgIyBzZXQgdGV4dCBzaXplIHRvIDIwDQoNCiAgICAgICAgICAjIE1vdmUgdGhlIGxlZ2VuZCBhcm91bmQgdG8gd2l0aGluIHRoZSBwYW5lbCBzcGFjZQ0KICAgICAgICAgIGxlZ2VuZC5qdXN0aWZpY2F0aW9uID0gYygwLDEpLA0KICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC4wMiwwLjk1KSwNCiAgICAgICAgICBsZWdlbmQuZGlyZWN0aW9uID0gImhvcml6b250YWwiLCANCg0KICAgICAgICAgICMgVXBkYXRlIHRoZSBwYW5lbCB0byBkcm9wIHRoZSBtaW5vciBheGlzIGdyaWQgbGluZXMNCiAgICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLCAgICAgICAgDQoNCiAgICAgICAgICAjIFVzZSBhIGJsYWNrIGxpbmUgZm9yIHRoZSBheGVzDQogICAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIpLA0KICAgICAgICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChjb2xvdXIgPSAiYmxhY2siLCBmYWNlPSJib2xkIiksDQogICAgICAgICApICsNCg0KICAgICMgQWRkIGxhYmVscyB0byBvdXIgcGxvdA0KICAgIGxhYnModGl0bGUgPSAiTWVhbiBjYXNlcyBvZiBDT1ZJRC0xOSBpbiBhIDE0LWRheSB3aW5kb3cgYWNyb3NzIHRvcCA0IE9udGFyaW8gUHVibGljIEhlYWx0aCBVbml0c1xuIiwNCiAgICAgICAgIHggPSAiXG5XaW5kb3cgZGF0ZSIsDQogICAgICAgICB5ID0gIk1lYW4gY2FzZXMgaW4gMTQtZGF5IHdpbmRvd1xuIiwNCiAgICAgICAgIGNvbG91ciA9ICJQdWJsaWMgSGVhbHRoIFVuaXQiLA0KICAgICAgICAgY2FwdGlvbiA9ICIqMTQtZGF5IHJvbGxpbmcgbWVhbiB3aXRoIGRhdGUgYXMgc3RhcnQgb2YgdGhlIHdpbmRvdyIpICsNCg0KICAgICMgMy4gU2NhbGluZw0KICAgICMjIyAyLjIuMSBTdGFydCBsb29raW5nIGF0IGRhdGEgZnJvbSBEZWNlbWJlciAyMDIwIG9ud2FyZHMNCiAgICBzY2FsZV94X2RhdGUoLi4uID0gYyhhcy5EYXRlKCIyMDIwLTEyLTAxIiksICAgICAgICAgICAgICAgICAgICAgICAgICMgU2V0IGEgc3RhcnQgZGF0ZSBmb3Igb3VyIGxpbWl0DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgYXMuRGF0ZShtYXgoY292aWRfcGh1X3dpbmRvdy5kZiRzdGFydF9kYXRlKSkpLCAjIElkZW50aWZ5IHRoZSBsYXN0IGRhdGUgYW5kIHVzZSB0aGF0DQogICAgICAgICAgICAgICAgIC4uLiA9ICIxIG1vbnRoIiwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBIb3cgd2lsbCB3ZSBicmVhayB1cCB0aGUgZGF0ZXM/DQogICAgICAgICAgICAgICAgIC4uLiA9ICIlYi0lWSIpICsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBIb3cgd2lsbCB3ZSBmb3JtYXQgbGFiZWxzDQoNCiAgICAjIDQuIEdlb21zDQogICAgZ2VvbV9saW5lKGxpbmV3aWR0aD0xKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgMi4yLjEuMSBBZGp1c3QgeW91ciBheGlzIHRleHQgd2l0aCB0aGUgYGVsZW1lbnRfdGV4dCgpYCBmdW5jdGlvbg0KDQpBdCB0aGlzIHBvaW50IHlvdSdsbCBub3RpY2UgdGhhdCBvdXIgeC1heGlzIHRleHQgaXMgYWxzbyBwcmV0dHkgdW5jbGVhbi4gTGV0J3MgcmV2aXNpdCB0aGUgYGF4aXMudGV4dC54YCBjb21wb25lbnQgb2YgdGhlbWUgdG8gZGVhbCB3aXRoIHRoaXMuIFRoZXJlIGFyZSBhIGZldyB0aGluZ3Mgd2UgY2FuIGluZmx1ZW5jZSB3aXRoIHRoaXMgYGVsZW1lbnRfdGV4dCgpYCBpbmNsdWRpbmc6DQoNCi0gICBgYW5nbGVgOiB1c2UgdGhpcyB0byByb3RhdGUgdGV4dCBmcm9tIGEgaG9yaXpvbnRhbCBwb3NpdGlvbiwgaW4gYSBjb3VudGVyLWNsb2NrLXdpc2UgZGlyZWN0aW9uLg0KDQotICAgYHZqdXN0YCBhbmQgYGhqdXN0YDogdGhlICoqdioqZXJ0aWNhbCBhbmQgKipoKipvcml6b250YWwganVzdGlmaWNhdGlvbiBvZiB5b3VyIHRleHQgYXMgYSB2YWx1ZSBmcm9tIDAgdG8gMSwgd2hlcmUgMC41IGlzICJjZW50ZXJlZCIuDQoNCi0gICBgZmFtaWx5YDogZGV0ZXJtaW5lIHRoZSBmb250IHVzZWQNCg0KLSAgIGBmYWNlYDogZGV0ZXJtaW5lIHRoZSBmb250IGZhY2UgKHBsYWluLCBib2xkLCBpdGFsaWMsIGJvbGQuaXRhbGljKQ0KDQotICAgYHNpemVgLCBgbGluZWhlaWdodGAsIGBjb2xvcmAsIGBjb2xvdXJgOiBhbHRlciBvdGhlciBjaGFyYWN0ZXJpc3RpY3Mgb2YgeW91ciB0ZXh0IGRpc3BsYXkNCg0KLSAgIGBkZWJ1Z2A6IGEgaGFuZHkgdG9vbCB0aGF0IGRyYXdzIGEgYm9yZGVyIGFyb3VuZCB5b3VyIGNvbXBsZXRlIHRleHQgYXJlYSBhbmQgYSBwb2ludCB3aGVyZSBlYWNoIGxhYmVsIGlzIGFuY2hvcmVkLiBHcmVhdCBmb3IgaGVscGluZyB0byB0d2VhayBwYXJhbWV0ZXJzIHRvIGdldCB0aGF0ICJwZXJmZWN0IiBsb29rIG9uIHlvdXIgZmlndXJlcyBidXQgbm90IG1lYW50IHRvIHJlbWFpbiBpbiB0aGUgZmluYWwgZmlndXJlLg0KDQpMZXQncyBmaXggdXAgb3VyIGN1cnJlbnQgdmlzdWFsaXphdGlvbiBieSByb3RhdGluZyBvdXIgdGV4dCBhbmQgcmlnaHQtanVzdGlmeWluZyBpdC4NCg0KYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD0xMH0NCg0KIyBCdWlsZCBvdXIgcGxvdA0KY292aWRfcGh1X3dpbmRvdy5kZiAlPiUgDQogICMgRmlsdGVyIGZvciB0aGUgdG9wIDUgaW5mZWN0ZWQgUEhVcw0KICBmaWx0ZXIocHVibGljX2hlYWx0aF91bml0ICVpbiUgcGh1X2J5X3RvdGFsX2Nhc2VzX2Rlc2NbMTo0XSkgJT4lIA0KICANCiAgIyByZWRpcmVjdCB0aGUgZmlsdGVyZWQgcmVzdWx0IHRvIGdncGxvdA0KICAjIDEuIERhdGENCiAgZ2dwbG90KC4pICsNCiAgICAjIDIuIEFlc3RoZXRpY3MNCiAgICBhZXMoeCA9IHN0YXJ0X2RhdGUsIHkgPSB3aW5kb3dfbWVhbiwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgU2V0IG91ciB4IGFuZCB5IGF4ZXMNCiAgICAgICAgY29sb3VyID0gZmN0X3Jlb3JkZXIocHVibGljX2hlYWx0aF91bml0LCB3aW5kb3dfbWVhbiwgLmRlc2M9VFJVRSkpICsgICMgUmVvcmRlciBvdXIgUEhVcw0KDQogICAgIyBUaGVtZSBlbGVtZW50cw0KICAgICMgU3RhcnQgd2l0aCBhIGJhc2UgdGhlbWUNCiAgICB0aGVtZV9taW5pbWFsKCkgKw0KDQogICAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTIwKSwgIyBzZXQgdGV4dCBzaXplIHRvIDIwDQoNCiAgICAgICAgICAjIE1vdmUgdGhlIGxlZ2VuZCBhcm91bmQgdG8gd2l0aGluIHRoZSBwYW5lbCBzcGFjZQ0KICAgICAgICAgIGxlZ2VuZC5qdXN0aWZpY2F0aW9uID0gYygwLDEpLA0KICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC4wMiwwLjk1KSwNCiAgICAgICAgICBsZWdlbmQuZGlyZWN0aW9uID0gImhvcml6b250YWwiLCANCg0KICAgICAgICAgICMgVXBkYXRlIHRoZSBwYW5lbCB0byBkcm9wIHRoZSBtaW5vciBheGlzIGdyaWQgbGluZXMNCiAgICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLCAgICAgICAgDQoNCiAgICAgICAgICAjIFVzZSBhIGJsYWNrIGxpbmUgZm9yIHRoZSBheGVzDQogICAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIpLA0KICAgICAgICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChjb2xvdXIgPSAiYmxhY2siLCBmYWNlPSJib2xkIiksDQogICAgICAgICAgDQogICAgICAgICAgIyMjIDIuMi4xLjEgQWRqdXN0IHRoZSB4LWF4aXMgdGV4dA0KICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gLi4uLCAgIyBSb3RhdGUgOTANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoanVzdCA9IC4uLiwgICAjIFJpZ2h0LWp1c3RpZnkNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2anVzdCA9IC4uLikgIyBDZW50cmUgdGV4dCAidmVydGljYWxseSIgb24gYXhpcyB0aWNrDQogICAgICAgICApICsNCg0KICAgICMgQWRkIGxhYmVscyB0byBvdXIgcGxvdA0KICAgIGxhYnModGl0bGUgPSAiTWVhbiBjYXNlcyBvZiBDT1ZJRC0xOSBpbiBhIDE0LWRheSB3aW5kb3cgYWNyb3NzIHRvcCA0IE9udGFyaW8gUHVibGljIEhlYWx0aCBVbml0c1xuIiwNCiAgICAgICAgIHggPSAiXG5XaW5kb3cgZGF0ZSIsDQogICAgICAgICB5ID0gIk1lYW4gY2FzZXMgaW4gMTQtZGF5IHdpbmRvd1xuIiwNCiAgICAgICAgIGNvbG91ciA9ICJQdWJsaWMgSGVhbHRoIFVuaXQiLA0KICAgICAgICAgY2FwdGlvbiA9ICIqMTQtZGF5IHJvbGxpbmcgbWVhbiB3aXRoIGRhdGUgYXMgc3RhcnQgb2YgdGhlIHdpbmRvdyIpICsNCg0KICAgICMgMy4gU2NhbGluZw0KICAgICMgU3RhcnQgbG9va2luZyBhdCBkYXRhIGZyb20gRGVjZW1iZXIgMjAyMCBvbndhcmRzDQogICAgc2NhbGVfeF9kYXRlKGxpbWl0cyA9IGMoYXMuRGF0ZSgiMjAyMC0xMi0wMSIpLCAgICAgICAgICAgICAgICAgICAgICAgICAjIFNldCBhIHN0YXJ0IGRhdGUgZm9yIG91ciBsaW1pdA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzLkRhdGUobWF4KGNvdmlkX3BodV93aW5kb3cuZGYkc3RhcnRfZGF0ZSkpKSwgIyBJZGVudGlmeSB0aGUgbGFzdCBkYXRlIGFuZCB1c2UgdGhhdA0KICAgICAgICAgICAgICAgICBkYXRlX2JyZWFrcyA9ICIxIG1vbnRoIiwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBIb3cgd2lsbCB3ZSBicmVhayB1cCB0aGUgZGF0ZXM/DQogICAgICAgICAgICAgICAgIGRhdGVfbGFiZWxzID0gIiViLSVZIikgKyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIEhvdyB3aWxsIHdlIGZvcm1hdCBsYWJlbHMNCg0KICAgICMgNC4gR2VvbXMNCiAgICBnZW9tX2xpbmUobGluZXdpZHRoPTEpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAyLjIuMiBSZWxhYmVsIGNvbnRpbnVvdXMgYXhpcyB0aWNrcyBieSBhbHRlcmluZyBgbGltaXRzYCBhbmQgYGJyZWFrc2ANCg0KTXVjaCBvZiB5b3VyIHF1YW50aXRhdGl2ZSBkYXRhIHdpbGwgdXN1YWxseSBjb21lIGFzIGEgY29udGludW91cyBzZXJpZXMgb2YgdmFsdWVzLiBXZSd2ZSBwbGF5ZWQgYXJvdW5kIHdpdGggdGhlc2Ugc2NhbGVzIGJlZm9yZSB1c2luZyBgc2NhbGVfKl9sb2cxMGAgaW4gbGVjdHVyZSBhbmQgYXNzaWdubWVudC4gU2ltaWxhcmx5LCB3ZSBjYW4gYWx0ZXIgY29udGludW91cyBheGVzIHdpdGhvdXQgbmVjZXNzYXJpbHkgdHJhbnNmb3JtaW5nIHRoZW0uIFRoaXMgaXMgYWNjb21wbGlzaGVkIHZpYSB0aGUgYHNjYWxlXypfY29udGludW91cygpYCBsYXllci4gV2l0aCB0aGVzZSB0eXBlcyBvZiBsYXllcnMsIHdlIGhhdmUgYWNjZXNzIHRvIHBhcmFtZXRlcnMgbGlrZToNCg0KLSAgIGBicmVha3NgLCBgbWlub3JfYnJlYWtzYDogYSBudW1lcmljIHZlY3RvciBvZiBwb3NpdGlvbnMgT1IgYSBmdW5jdGlvbiB0aGF0IHRha2VzIHRoZSBsaW1pdHMgYXMgaW5wdXQgYW5kIHJldHVybnMgYnJlYWtzIGFzIG91dHB1dCBmb3IgdGhlIHBhcmFtZXRlciBzcGVjaWZpZWQuDQoNCi0gICBgbi5icmVha3NgOiBhbiBpbnRlZ2VyIHRvIHN1Z2dlc3QgdGhlIG51bWJlciBvZiBtYWpvciBicmVha3MuIFRoZSBwbG90dGluZyBhbGdvcml0aG0gbWF5IGFsdGVyIHRoaXMgdmFsdWUgdG8gZW5zdXJlIG5pY2UgYnJlYWsgbGFiZWxzLiBUaGlzIHdpbGwgb25seSB3b3JrIGlmIGBicmVha3MgPSB3YWl2ZXIoKWAgKHRoZSBkZWZhdWx0IGZvciBgYnJlYWtzYCkuDQoNCi0gICBgbGFiZWxzYDogYSBjaGFyYWN0ZXIgdmVjdG9yIG1hdGNoaW5nIGxhYmVscyB0byB0aGUgbWFqb3IgYnJlYWtzLg0KDQotICAgYGxpbWl0c2A6IGEgbnVtZXJpYyB2ZWN0b3IgYGMobG93ZXIsIHVwcGVyKWANCg0KTGV0J3MgYnJlYWsgb3VyIHktYXhpcyBpbnRvIG1ham9yIHRpY2stbWFya3Mgb2YgZXZlcnkgNTAwIGNhc2VzIGJ5IGFsdGVyaW5nIGBzY2FsZV95X2NvbnRpbnVvdXMoKWAgd2l0aCB0aGUgYHNlcSgpYCBmdW5jdGlvbi4gQXQgdGhlIHNhbWUgdGltZSwgbGV0J3MgcmVtb3ZlIHRoZSB0aXRsZSBmcm9tIG91ciBsZWdlbmQgYnkgc2V0dGluZyB0aGUgZ3VpZGUgaW4gYGxhYnMoKWAgdG8gYSBgTlVMTGAgdmFsdWUuDQoNCmBgYHtyLCBmaWcud2lkdGg9MjAsIGZpZy5oZWlnaHQ9MTB9DQoNCiMgQnVpbGQgb3VyIHBsb3QgYW5kIHNhdmUgdG8gYW4gb2JqZWN0IGZvciBsYXRlciB1c2UNCnBodV93aW5kb3cucGxvdCA8LSBjb3ZpZF9waHVfd2luZG93LmRmICU+JSANCiAgIyBSZW9yZGVyIHRoZSBQSFUgZmFjdG9yIGhlcmUNCiAgbXV0YXRlKHB1YmxpY19oZWFsdGhfdW5pdCA9IGZjdF9yZW9yZGVyKHB1YmxpY19oZWFsdGhfdW5pdCwgd2luZG93X21lYW4sIC5kZXNjPVRSVUUpKSAlPiUgDQogICMgRmlsdGVyIGZvciB0aGUgdG9wIDUgaW5mZWN0ZWQgUEhVcw0KICBmaWx0ZXIocHVibGljX2hlYWx0aF91bml0ICVpbiUgcGh1X2J5X3RvdGFsX2Nhc2VzX2Rlc2NbMTo0XSkgJT4lIA0KICANCiAgIyByZWRpcmVjdCB0aGUgZmlsdGVyZWQgcmVzdWx0IHRvIGdncGxvdA0KICAjIDEuIERhdGENCiAgZ2dwbG90KC4pICsNCiAgICAjIDIuIEFlc3RoZXRpY3MNCiAgICBhZXMoeCA9IHN0YXJ0X2RhdGUsIHkgPSB3aW5kb3dfbWVhbiwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgU2V0IG91ciB4IGFuZCB5IGF4ZXMNCiAgICAgICAgY29sb3VyID0gcHVibGljX2hlYWx0aF91bml0KSArICANCg0KICAgICMgU3RhcnQgd2l0aCBhIGJhc2UgdGhlbWUNCiAgICB0aGVtZV9taW5pbWFsKCkgKw0KDQogICAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTIwKSwgIyBzZXQgdGV4dCBzaXplIHRvIDIwDQogICAgICAgICAgDQogICAgICAgICAgIyBNb3ZlIHRoZSBsZWdlbmQgYXJvdW5kIHRvIHdpdGhpbiB0aGUgcGFuZWwgc3BhY2UNCiAgICAgICAgICBsZWdlbmQuanVzdGlmaWNhdGlvbiA9IGMoMCwxKSwNCiAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSBjKDAuMDIsMC45NSksDQogICAgICAgICAgbGVnZW5kLmRpcmVjdGlvbiA9ICJob3Jpem9udGFsIiwgDQogICAgICAgICAgDQogICAgICAgICAgIyBVcGRhdGUgdGhlIHBhbmVsIHRvIGRyb3AgdGhlIG1pbm9yIGF4aXMgZ3JpZCBsaW5lcw0KICAgICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgICAgDQogICAgICAgICAgIyBVc2UgYSBibGFjayBsaW5lIGZvciB0aGUgYXhlcw0KICAgICAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siKSwNCiAgICAgICAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoY29sb3VyID0gImJsYWNrIiwgZmFjZT0iYm9sZCIpLA0KICAgICAgICAgIA0KICAgICAgICAgICMgQWRqdXN0IHRoZSB4LWF4aXMgdGV4dA0KICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsICAjIFJvdGF0ZSA5MA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhqdXN0ID0gMSwgICAjIFJpZ2h0LWp1c3RpZnkNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2anVzdCA9IDAuNSkgIyBDZW50cmUgdGV4dCAidmVydGljYWxseSIgb24gYXhpcyB0aWNrDQogICAgICAgICApICsNCg0KICAgICMgQWRkIGxhYmVscyB0byBvdXIgcGxvdA0KICAgIGxhYnModGl0bGUgPSAiTWVhbiBjYXNlcyBvZiBDT1ZJRC0xOSBpbiBhIDE0LWRheSB3aW5kb3cgYWNyb3NzIHRvcCA0IE9udGFyaW8gUHVibGljIEhlYWx0aCBVbml0c1xuIiwNCiAgICAgICAgIHggPSAiXG5XaW5kb3cgZGF0ZSIsDQogICAgICAgICB5ID0gIk1lYW4gY2FzZXMgaW4gMTQtZGF5IHdpbmRvd1xuIiwNCiAgICAgICAgIGNvbG91ciA9IE5VTEwsDQogICAgICAgICBjYXB0aW9uID0gIioxNC1kYXkgcm9sbGluZyBtZWFuIHdpdGggZGF0ZSBhcyBzdGFydCBvZiB0aGUgd2luZG93IikgKw0KDQogICAgIyAzLiBTY2FsaW5nDQogICAgIyBTdGFydCBsb29raW5nIGF0IGRhdGEgZnJvbSBKdWx5IDIwMjAgb253YXJkcw0KICAgIHNjYWxlX3hfZGF0ZShsaW1pdHMgPSBjKGFzLkRhdGUoIjIwMjAtMTItMDEiKSwgICAgICAgICAgICAgICAgICAgICAgICAgIyBTZXQgYSBzdGFydCBkYXRlIGZvciBvdXIgbGltaXQNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5EYXRlKG1heChjb3ZpZF9waHVfd2luZG93LmRmJHN0YXJ0X2RhdGUpKSksICMgSWRlbnRpZnkgdGhlIGxhc3QgZGF0ZSBhbmQgdXNlIHRoYXQNCiAgICAgICAgICAgICAgICAgZGF0ZV9icmVha3MgPSAiMSBtb250aCIsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgSG93IHdpbGwgd2UgYnJlYWsgdXAgdGhlIGRhdGVzPw0KICAgICAgICAgICAgICAgICBkYXRlX2xhYmVscyA9ICIlYi0lWSIpICsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBIb3cgd2lsbCB3ZSBmb3JtYXQgbGFiZWxzDQoNCiAgICAjIyMgMi4yLjIgQ2hhbmdlIG91ciB5LWF4aXMgYnJlYWtzDQogICAgLi4uICsNCg0KICAgICMgNC4gR2VvbXMNCiAgICBnZW9tX2xpbmUobGluZXdpZHRoPTEpDQoNCiMgcGxvdCBvdXIgb2JqZWN0IHRvIHN0YW5kYXJkIG91dHB1dA0KcGh1X3dpbmRvdy5wbG90DQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAyLjIuMyBSZWxhYmVsIGRpc2NyZXRlIGF4ZXMgYW5kIGxlZ2VuZCBsYWJlbHMgaW4gYHNjYWxlXypfZGlzY3JldGUoKWANCg0KRm9yIHZhcmlvdXMgcmVhc29ucywgeW91IG1heSBoYXZlIGNhdGVnb3JpY2FsIG9yIGdyb3VwZWQgZGF0YSB3aXRoIHVudXN1YWwgbmFtZXMuIEl0IG1heSBiZSBjb252ZW5pZW50IHRvIGNvZGUgeW91ciBkYXRhIHRoaXMgd2F5IGJ1dCBsZXR0aW5nIGBnZ3Bsb3QyYCBhc3NpZ24gdGhlc2UgdG8geW91ciBheGVzIG9yIGxhYmVscyBtYXkgbm90IGJlIHN1aXRhYmxlLiBJbnN0ZWFkLCB5b3UgY2FuIG1hbnVhbGx5IHJlbmFtZSB0aGVtIHVzaW5nIHRoZSBgbGFiZWxzYCBwYXJhbWV0ZXIgd2l0aCB5b3VyIHZhcmlvdXMgYHNjYWxlXypfZGlzY3JldGUoKWAgbGF5ZXJzLg0KDQpXaGVuIG1hbnVhbGx5IGxhYmVsaW5nIHlvdXIgY2F0ZWdvcmllcyBiZSBzdXJlIHRvIHN1cHBseSBhIHZlY3RvciB3aXRoIHRoZSBjb3JyZWN0IG51bWJlciBvZiBhcmd1bWVudHMgdG8gbWF0Y2ggdGhlIG51bWJlciBvZiBsZXZlbHMgaW4geW91ciBjYXRlZ29yaWVzIG9yIGdyb3Vwcy4NCg0KTGV0J3MgcmV2aXNpdCBzb21lIG9mIG91ciBhZ2UtZ3JvdXBlZCBkYXRhIGZyb20gbGFzdCB3ZWVrIHdoaWNoIHdhcyB2aXN1YWxpemVkIGFzICoqKmdyb3VwZWQgdmlvbGluIHBsb3Qgd2l0aCBpbnNldCBib3hwbG90cyoqKi4gUmVjYWxsIHRoYXQgb3VyIGRhdGEgd2FzIGxhYmVsbGVkIGJ5IHRoZSB2YXJpYWJsZSBgYWdlX2dyb3VwYCB1c2luZyAiMCB0byA0IiwgIjUgdG8gMTEiLCBldGMuIFdlJ2xsIG1vZGlmeSB0aG9zZSBpbiB0aGUgcGxvdCAocmF0aGVyIHRoYW4gdGhlIGRhdGEgZnJhbWUpIHRvIGEgZm9ybWF0IHRoYXQgbG9va3MgbGlrZSAiMC00IiwgIjUtMTEiLCBldGMuDQoNCmBgYHtyfQ0KIyBMZXQncyBicmllZmx5IHJldmlldyB0aGUgZGF0YXNldA0Kc3RyKGNvdmlkX2RlbW9ncmFwaGljc190b3RhbC5kZiwgZ2l2ZS5hdHRyID0gRkFMU0UpDQpgYGANCg0KYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD0xMH0NCg0KIyBCdWlsZCBhbmQgc2F2ZSB0aGUgcGxvdCBmb3IgbGF0ZXIgdXNlDQpkZW1vZ3JhcGhpY3MucGxvdCA8LSBjb3ZpZF9kZW1vZ3JhcGhpY3NfdG90YWwuZGYgJT4lIA0KICAjIFVuZ3JvdXAgdGhpcyBkYXRhZnJhbWUgdG8gY2xlYW4gaXQgdXAgYSBsaXR0bGUNCiAgdW5ncm91cCgpICU+JSANCiAgIyBGaWx0ZXIgZm9yIGN1bXVsYXRpdmUgZGF0YQ0KICBmaWx0ZXIocGVyaW9kID09ICJjdW11bGF0aXZlIikgJT4lIA0KICAjIFNlbGVjdCBmb3IganVzdCB0aGUgaW1wb3J0YW50IGNvbHVtbnMNCiAgc2VsZWN0KHB1YmxpY19oZWFsdGhfdW5pdCwgYWdlX2dyb3VwLCBwZXJjZW50X2Nhc2VzLCBwZXJjZW50X2hvc3BpdGFsaXphdGlvbnMpICU+JSANCiAgIyBQaXZvdCB0aGUgbW9kaWZpZWQgdGFibGUgdG8gY2FwdHVyZSB0aGUgInN0YXRfZ3JvdXAiIG9mIHBlcmNlbnRfY2FzZXMgdnMgcGVyY2VudF9ob3NwaXRhbGl6YXRpb25zDQogIHBpdm90X2xvbmdlcihjb2xzPWMoMyw0KSwgbmFtZXNfdG8gPSAic3RhdF9ncm91cCIsIHZhbHVlc190byA9ICJwZXJjZW50X1BIVV90b3RhbCIpICU+JSANCiAgDQogICMgUGxvdCB0aGUgZGF0YSBhcyBhIGdyb3VwZWQgdmlvbGluIHBsb3Qgd2l0aCBpbnNldCBib3hwbG90DQogIA0KICAjIDEuIERhdGENCiAgZ2dwbG90KC4pICsNCiAgICAjIDIuIEFlc3RoZXRpY3MNCiAgICBhZXMoeD1hZ2VfZ3JvdXAsIHkgPSBwZXJjZW50X1BIVV90b3RhbCkgKw0KDQogICAgIyBTdGFydCB3aXRoIGEgYmFzZSB0aGVtZQ0KICAgIHRoZW1lX21pbmltYWwoKSArDQoNCiAgICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MjApLCAjIHNldCB0ZXh0IHNpemUgdG8gMjANCiAgICAgICAgICANCiAgICAgICAgICAjIE1vdmUgdGhlIGxlZ2VuZCBhcm91bmQgdG8gd2l0aGluIHRoZSBwYW5lbCBzcGFjZQ0KICAgICAgICAgIGxlZ2VuZC5qdXN0aWZpY2F0aW9uID0gYygwLDEpLA0KICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC4wMiwwLjk1KSwNCiAgICAgICAgICBsZWdlbmQuZGlyZWN0aW9uID0gImhvcml6b250YWwiLCANCiAgICAgICAgICANCiAgICAgICAgICAjIFVwZGF0ZSB0aGUgcGFuZWwgdG8gZHJvcCB0aGUgbWlub3IgYXhpcyBncmlkIGxpbmVzDQogICAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICANCiAgICAgICAgICAjIFVzZSBhIGJsYWNrIGxpbmUgZm9yIHRoZSBheGVzDQogICAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIpLA0KICAgICAgICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChjb2xvdXIgPSAiYmxhY2siLCBmYWNlPSJib2xkIiksDQogICAgICAgICApICsNCiAgICANCiAgICAjIEFkZCBsYWJlbHMgdG8gdGhlIHBsb3QNCiAgICBsYWJzKHRpdGxlID0gIlBlcmNlbnQgY2FzZXMgYW5kIGhvc3BpdGFsaXphdGlvbnMgYnkgcHJvcG9ydGlvbiBwZXIgUEhVIGFjcm9zcyBhZ2UgZ3JvdXAgLSBjdW11bGF0aXZlIGFjcm9zcyBwYW5kZW1pYyIsDQogICAgICAgICB4ID0gIlxuQWdlIGdyb3VwIiwNCiAgICAgICAgIHkgPSAiUHJvcG9ydGlvbiBvZiByZXBvcnRlZCBQSFUgZGF0YVxuIiwNCiAgICAgICAgIGNhcHRpb24gPSAiXG4qQWdlIGdyb3VwIHZhbHVlcyBhcmUgY2FsY3VsYXRlZCBhcyBhIHBlcmNlbnRhZ2Ugb2YgdG90YWwgY2FzZXMgb3IgaG9zcGl0YWxpemF0aW9ucyB3aXRoaW4gYSBQSFUiKSArDQoNCiAgICAjIDMuIFNjYWxpbmcNCiAgICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygwLCAwLjUpKSArICAgICAgICAgIyBTZXQgdGhlIGxpbWl0cyBvZiBvdXIgeS1heGlzIA0KICAgIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzPWMoImJsYWNrIiwgImJsYWNrIikpKyAjIHdlJ2xsIG5lZWQgdGhpcyB0byBmaXggb3VyIGJveHBsb3Qgb3V0bGluZXMNCg0KICAgICMjIyAyLjIuMyBTZXQgdGhlIGxhYmVscyBvZiBvdXIgeC1heGlzIGNhdGVnb3JpZXMNCiAgICBzY2FsZV94X2Rpc2NyZXRlKC4uLj1jKCIwLTQiLCAiNS0xMSIsICIxMi0xOSIsICIyMC0zOSIsICI0MC01OSIsICI2MC03OSIsICI4MCsiKSkrDQoNCiAgICAjIDQuIERhdGENCiAgICAjIG11bHRpLWZhY3RvciB2aW9saW4gcGxvdHMgYnV0IGtlZXAgdGhlIHdpZHRoIGNvbnNpc3RlbnQNCiAgICBnZW9tX3Zpb2xpbihzY2FsZT0id2lkdGgiLCBhZXMoZmlsbD1zdGF0X2dyb3VwKSkgKyANCg0KICAgICMgQm94cGxvdCBidXQgc21hbGxlciB3aWR0aCBzbyB0aGV5IHJlc2lkZSAid2l0aGluIiB0aGUgdmlvbGluIHBsb3QNCiAgICBnZW9tX2JveHBsb3QoYWVzKGNvbG91ciA9IHN0YXRfZ3JvdXApLCB3aWR0aD0wLjIsIA0KICAgICAgICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoPTAuOSksIA0KICAgICAgICAgICAgICAgICBvdXRsaWVyLnNoYXBlPU5BKSArICMgUmVtb3ZlIHRoZSBvdXRsaWVycw0KDQogICAgIyBBZGQgaW4gYWxsIG9mIHRoZSBkYXRhIHBvaW50cw0KICAgICAgZ2VvbV9xdWFzaXJhbmRvbShkb2RnZS53aWR0aCA9IDAuODUsIGFlcyhncm91cD1zdGF0X2dyb3VwKSwgYWxwaGEgPSAwLjgpDQoNCiMgU2hvdyB0aGUgcGxvdA0KZGVtb2dyYXBoaWNzLnBsb3QNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgMi4zLjAgQWx0ZXIgeW91ciBsZWdlbmRzIHdpdGggdGhlIGBndWlkZWAgcGFyYW1ldGVyIG9yIGBndWlkZXMoKWAgbGF5ZXINCg0KTmVhcmx5IHRoZXJlIHdpdGggdXBkYXRpbmcgdGhpcyBwbG90ISBXZSd2ZSByZWxhYmVsZWQgdGhlIHRoZSB4LWF4aXMgY2F0ZWdvcmllcyBidXQgb3VyIGxlZ2VuZCB0aXRsZSBpc24ndCBxdWl0ZSB0aGVyZS4gUHJldmlvdXNseSB3ZSB1c2VkIHRoZSBgbGFicygpYCBsYXllciB0byBoYW5kbGUgdGhpcyBhc3BlY3QgYnV0IHRoaXMgdGltZSBhcm91bmQgd2UgcmVhbGx5IHdhbnQgdG8gYWxzbyBhbHRlciB0aGUgbGFiZWxzIG9mIG91ciBkYXRhIGNhdGVnb3JpZXMgdG8gIiUgY2FzZXMiIGFuZCAiJSBob3NwaXRhbGl6YXRpb25zIi4gQmVmb3JlIHdlIGdldCBpbnRvIHRoYXQsIGxldCdzIHRhbGsgYSBsaXR0bGUgbW9yZSBhYm91dCBsZWdlbmRzLg0KDQpOb3JtYWxseSB5b3UgY2FuIGxldCBgZ2dwbG90MmAgdGFrZSB0aGUgd2hlZWwgYW5kIGF1dG9tYXRpY2FsbHkgZ2VuZXJhdGUgZ3VpZGVzIGZvciB5b3UuIFdoZW5ldmVyIHlvdSBzZXQgY29sb3VyL2ZpbGwvbGluZXR5cGUgZXRjIGluIHlvdXIgYWVzdGhldGljcywgdGhpcyB3aWxsIGdlbmVyYXRlIGEgbGVnZW5kLiBXaGVuIHRoZSBncm91cHMgYXJlIG1hcHBlZCBpbiB0aGUgc2FtZSB3YXkgKGkuZS4gdGhlIHNhbWUgbGFiZWxzISkgYmV0d2VlbiBkaWZmZXJlbnQgYWVzdGhldGljcywgdGhlIGxlZ2VuZHMgbWF5IGJlIGNvbWJpbmVkLg0KDQpUaGVyZSB3aWxsIGJlIGluc3RhbmNlcywgaG93ZXZlciwgd2hlbiB5b3UgbmVlZCB0byBhZGp1c3QgeW91ciBsZWdlbmQgb3IgZ2V0IHJpZCBvZiBpdCBhbGwgdG9nZXRoZXIuIFRoaXMgY291bGQgcmFuZ2UgZnJvbSB0aXRsZXMsIHRvIGNvbWJpbmluZyB5b3VyIGd1aWRlcyBhY3Jvc3MgZGlmZmVyZW50IGFlc3RoZXRpY3MgY29tbWFuZHMuIFRoZXJlIGFyZSBhIG51bWJlciBvZiB3YXlzIHRvIGFjaGlldmUgdGhlIHNhbWUgcmVzdWx0IHdoZW4gd29ya2luZyB3aXRoIGd1aWRlcyBhbmQgd2UnbGwgZ28gdGhyb3VnaCBhIG51bWJlciBvZiBleGFtcGxlcy4gRmlyc3QsIGhvd2V2ZXIsIHdlIHNob3VsZCBkaXNjdXNzIHRoZSAqdHlwZXMqIG9mIGxlZ2VuZHM6DQoNCnwgZ3VpZGUgICAgICAgICAgICAgICB8IHNob3J0IGNhbGwgIHwgRGVzY3JpcHRpb24gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8Oi0tLS0tLS0tLS0tLS0tLS0tLXw6LS0tLS0tLS0tLS0tLS0tLS0tfDotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfA0KfCBndWlkZV9sZWdlbmQoKSAgICAgIHwgbGVnZW5kICAgICAgfCBUaGUgYmFzZSBwcm90b3R5cGUgb2YgdGhlIGxlZ2VuZCB3aGljaCBpbnRlZ3JhdGVzIGhvdyBnZW9tcyBhcmUgbWFwcGVkIGludG8gdmFsdWVzLiAgICAgICAgICAgIHwNCnwgZ3VpZGVfYmlucygpICAgICAgICB8IGJpbnMgICAgICAgIHwgQSBiaW5uZWQgdmVyc2lvbiBvZiBsZWdlbmRzIHdoaWNoIHBsYWNlcyB0aWNrcyBiZXR3ZWVuIGtleXMgYW5kIGhhcyBpdHMgb3duIHNtYWxsIGF4aXMgICAgICAgICB8DQp8IGd1aWRlX2NvbG91cmJhcigpICAgfCBjb2xvdXJiYXIgICB8IEZvciBtYXBwaW5nIGNvbnRpbm91cyBjb2xvdXIvZmlsbCBzY2FsZXMgZnJvbSB1c2luZyBgc2NhbGVfZmlsbF8qKClgIGFuZCBgc2NhbGVfY29sb3VyXyooKWAuICAgfA0KfCBndWlkZV9jb2xvdXJzdGVwcygpIHwgY29sb3Vyc3RlcHMgfCBBIHZlcnNpb24gb2YgZ3VpZGVfY29sb3VyYmFyKCkgZXhjZXB0IGZvciBiaW5uZWQgY29sb3VyIGFuZCBmaWxsIHNjYWxlcyByYXRoZXIgdGhhbiBncmFkaWVudHMuIHwNCnwgbm9uZSAgICAgICAgICAgICAgICB8IE5BICAgICAgICAgIHwgU3VwcHJlc3MgdGhlIGxlZ2VuZCBhcyBzcGVjaWZpZWQgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQoNCldlIGJyaWVmbHkgc2F3IHRoZSB1c2Ugb2YgYSBjb2xvdXJiYXIgaW4gb3VyIGxhc3QgbGVjdHVyZSB3aGVuIHVzaW5nIGEgY29udGludW91cyB2YXJpYWJsZSB0byBzZXQgdGhlIGNvbG91ciBvZiBvdXIgYmFycGxvdHMuIEVhY2ggdHlwZSBoYXMgaXQncyBvd24gdXNlIGRlcGVuZGluZyBvbiBob3cgeW91IHdhbnQgdG8gZGVzY3JpYmUgeW91ciBkYXRhLiBXaXRoaW4gZWFjaCBvZiB0aGUgZ3VpZGUgdHlwZXMsIHlvdSBjYW4gdXBkYXRlIHBhcmFtZXRlcnMgYWJvdXQgdGV4dCB3aXRoaW4gdGhlIGxlZ2VuZC4NCg0KfCBDb21wb25lbnQgfCBTdWItY29tcG9uZW50cyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8Oi0tLS0tLS0tLS0tLS0tLS0tLS0tLXw6LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwNCnwgdGl0bGUgICAgIHwgbmFtZSwgcG9zaXRpb24sIHRoZW1lLCBoanVzdCwgdmp1c3QgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBsYWJlbCAgICAgfCBuYW1lLCBwb3NpdGlvbiwgdGhlbWUsIGhqdXN0LCB2anVzdCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IGtleSAgICAgICB8IHdpZHRoLCBoZWlnaHQgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgb3JkZXIgICAgIHwgeW91IGNhbiBkZXRlcm1pbmUgdGhlIG9yZGVyIG9mIHRoZSBndWlkZSBhbW9uZ3N0IG90aGVycyB1c2luZyBpbnRlZ2VycyBbMTo5OV0uIDAgc2V0cyBvcmRlciBieSBhbiBhbGdvcml0aG0gfA0KfCBvdGhlciAgICAgfCBkaXJlY3Rpb24gb2YgZ3VpZGUsIG51bWJlciBvZiByb3dzL2NvbHMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQoNClNvIHdoZXJlIGNhbiB5b3UgdXNlIHRoZXNlIG1ldGhvZHM/DQoNCiMjIyAyLjMuMSBVc2UgYHNjYWxlXyooKWAgdG8gc2V0IGd1aWRlIHBhcmFtZXRlcnMNCg0KV2l0aGluIGVhY2ggYHNjYWxlXyooKWAgeW91IGRlY2xhcmUgeW91IGNhbiBzZXQgdGhlIHBhcmFtZXRlciBgZ3VpZGVgIHRvIG9uZSBvZiB0aGUgYWJvdmUgZ3VpZGUgdHlwZXMuIFRvIGV4Y2x1ZGUgYSBsZWdlbmQgZm9yIHRoYXQgcGFydGljdWxhciB0eXBlLCBzZXQgdGhlIHZhbHVlIHRvIGBub25lYC4NCg0KU29tZSBsYXllciBvcHRpb25zIHlvdSBtYXkgd29yayB3aXRoIGhlcmUgYXJlIGBzY2FsZV9maWxsX2Rpc2NyZXRlKClgICwgYHNjYWxlX3NoYXBlX21hbnVhbCgpYCBhbmQgYHNjYWxlX2NvbG91cl9jb250aW51b3VzKClgDQoNCi0gICBzb21lIG9mIHdoaWNoIHdlJ3ZlIHNlZW4gaW4gcHJldmlvdXMgbGVjdHVyZXMuIE5vdGljZSB0aGF0IGBmaWxsYCwgYHNoYXBlYCBhbmQgYGNvbG91cmAgYXJlIGFsbCBbYWVzdGhldGljIHBhcmFtZXRlcnNdKGh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9hZXNfZ3JvdXBfb3JkZXIuaHRtbCkgd2UgY2FuIFtjaGFuZ2UgaW4gb3VyIGRhdGEgbWFwcGluZ10oaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvYXJ0aWNsZXMvZ2dwbG90Mi1zcGVjcy5odG1sKS4NCg0KTGV0J3MgdXBkYXRlIG91ciBmaWxsIGd1aWRlIHRvIGNoYW5nZSB0aGUgbGVnZW5kIHRpdGxlIHRvICJEYXRhIGNhdGVnb3J5IiBhbmQgcmVsYWJlbCBvdXIgY2F0ZWdvcmllcyB0byAiJSBjYXNlcyIgYW5kICIlIGhvc3BpdGFsaXphdGlvbnMiIGFzIHByZXZpb3VzbHkgZGlzY3Vzc2VkLg0KDQpgYGB7ciwgZmlnLndpZHRoPTIwLCBmaWcuaGVpZ2h0PTEwfQ0KIyBBZGp1c3QgdGhlIGZpbGwgc2NhbGUgbGF5ZXIgZm9yIHRoZSBkZW1vZ3JhcGhpY3MgcGxvdA0KZGVtb2dyYXBoaWNzLnBsb3QgKw0KICAgICMjIyAyLjMuMSBTZXQgdGhlIGZpbGwgZ3VpZGUgZGV0YWlscw0KICAgIC4uLihuYW1lID0gIkRhdGEgY2F0ZWdvcnkiLCAgICAgICAgICAgICAgIyBHdWlkZSBuYW1lDQogICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCIlIGNhc2VzIiwgIiUgaG9zcGl0YWxpemF0aW9ucyIpKSAgICMgUmVsYWJlbCB0aGUgY2F0ZWdvcmllcw0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgMi4zLjIgVXNlIHRoZSBgZ3VpZGVzKClgIGxheWVyIHRvIG1hbmlwdWxhdGUgbXVsdGlwbGUgZ3VpZGVzDQoNCldoaWxlIG91ciBvdXRwdXQgaXMgbmVhcmx5IGNvcnJlY3QsIHRoZXJlIGlzIHN0aWxsIGEgcHJvYmxlbSEgTm93IGhhdmUgdHdvIHNldHMgb2YgbGVnZW5kcyEgSWYgeW91IGxvb2sgY2FyZWZ1bGx5IGF0IHRoZSBnZ3Bsb3QgY29kZSwgeW91J2xsIHNlZSB0aGF0IHdlIHNldCBhZXN0aGV0aWNzIGluIHRocmVlIHBsYWNlczoNCg0KLSAgIGBnZW9tX3Zpb2xpbihzY2FsZT0id2lkdGgiLCBhZXMoZmlsbD1zdGF0X2dyb3VwKSlgDQoNCi0gICBgZ2VvbV9ib3hwbG90KGFlcyhjb2xvdXIgPSBzdGF0X2dyb3VwKS4uLmANCg0KLSAgIGBnZW9tX3F1YXNpcmFuZG9tKGRvZGdlLndpZHRoID0gMC44NSwgYWVzKGdyb3VwPXN0YXRfZ3JvdXApLCBhbHBoYSA9IDAuOClgDQoNCkFjcm9zcyAzIGdlb21zIHdlJ3ZlIGdlbmVyYXRlZCAzIGFlc3RoZXRpYyBncm91cHM6IGBmaWxsYCwgYGNvbG91cmAgYW5kIGBncm91cGAuIFJlbWVtYmVyIHdoZW4gd2Ugc2FpZCB0aGF0IGBnZ3Bsb3RgIHdvdWxkIHRha2UgdGhlIHdoZWVsIGFuZCBnZW5lcmF0ZSBsZWdlbmQvZ3VpZGUgaW5mb3JtYXRpb24gYXV0b21hdGljYWxseT8gV2VsbCB0aGlzIGlzIGEgY2FzZSB3aGVyZSBhbGwgdGhyZWUgYXJlIG1hcHBpbmcgYnkgdGhlIHNhbWUgdmFyaWFibGUgc28gdGhleSBnZXQgY29tYmluZWQgaW50byBhIHNpbmdsZSBsZWdlbmQuIFdoZW4gd2UgdG9vayB0aGUgdGltZSB0byBjaGFuZ2UgdGhlIGBmaWxsYCBndWlkZSwgaG93ZXZlciwgaXQgd2FzIGJyb2tlbiBhd2F5IGZyb20gdGhlIG90aGVyIHR3byAoYGdlb21fYm94cGxvdGAgYW5kIGBnZW9tX3F1YXNpcmFuZG9tYCkgZ3VpZGVzLg0KDQpJbiBhIGNhc2UgbGlrZSB0aGlzIHdlIHVzZSB0aGUgYGd1aWRlcygpYCBsYXllciB0byBzZXQgbXVsdGlwbGUgZ3VpZGVzIGF0IG9uY2UgdXNpbmcgdGhlIHNjYWxlIHR5cGVzIGFzIHBhcmFtZXRlcnMgaWUgY29sb3VyLCBzaXplLCBzaGFwZS4gTXVjaCBsaWtlIGBsYWJzKClgIGl0IGdpdmVzIHVzIGNlbnRyYWxpemVkIGFjY2VzcyB0byBndWlkZSBmb3JtYXQgYW5kIHNldHRpbmdzLCBhbGxvd2luZyB1cyB0byBxdWlja2x5IHJlY3RpZnkgb3VyIHByb2JsZW0uIEluIHRoaXMgY2FzZSwgd2UgcmVhbGx5IGRvbid0IG5lZWQgdGhlIGBncm91cGAgb3IgYGNvbG91cmAgYWVzdGhldGljcywgc28gd2UnbGwgc2ltcGx5IGdldCByaWQgb2YgdGhlbS4NCg0KYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD0xMH0NCiMgQWRqdXN0IHRoZSBmaWxsIHNjYWxlIGxheWVyIGZvciB0aGUgZGVtb2dyYXBoaWNzIHBsb3QNCmRlbW9ncmFwaGljcy5wbG90ICsNCiAgICAjIyMgMi4zLjIgVXNlIHRoZSBndWlkZXMoKSBsYXllciBhbmQgZ2V0IHJpZCBvZiB0aGUgc2NhbGVfZmlsbF9kaXNjcmV0ZSgpIGxheWVyDQogICAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQodGl0bGUgPSAiRGF0YSBjYXRlZ29yeSIpLCANCiAgICAgICAgICAgY29sb3VyID0gLi4uLCBncm91cCA9IC4uLikgKw0KICAgIA0KICAgICMgU2V0IHRoZSBmaWxsIGd1aWRlIGRldGFpbHMNCiAgICBzY2FsZV9maWxsX2Rpc2NyZXRlKGxhYmVscyA9IGMoIiUgY2FzZXMiLCAiJSBob3NwaXRhbGl6YXRpb25zIikpICAgIyBSZWxhYmVsIHRoZSBjYXRlZ29yaWVzDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCjo6OiB7LmFsZXJ0IC5hbGVydC1ibG9jayAuYWxlcnQtd2FybmluZ30NCioqVHJ5IHRvIG1pbmltaXplIHlvdXIgbGF5ZXJzOioqIEluIG91ciBhYm92ZSBleGFtcGxlIHdlIGhhZCB0byB1c2UgKipzY2FsZV9maWxsX2Rpc2NyZXRlKCkqKiBhbmQgKipndWlkZXMoKSoqIGJlY2F1c2Ugd2UgbmVlZGVkIHRvIG1hbmlwdWxhdGUgbXVsdGlwbGUgZ3VpZGVzIGJ1dCBvbmx5IGEgY291cGxlIGluIGEgdmVyeSBzaW1wbGUgd2F5LiBUaGlzIGZvcm1hdCwgaG93ZXZlciwgbWlnaHQgbm90IGFsd2F5cyBiZSB0aGUgYmVzdCBjaG9pY2UuIEZvciBpbnN0YW5jZSwgc3VwcG9zZSB5b3Ugd2FudGVkIHRvIGV4cGxpY2l0bHkgY2hvb3NlIHlvdXIgdmlvbGluIGNvbG91cnM/IFRoZW4gYSAqKnNjYWxlX2ZpbGxfbWFudWFsKCkqKiBsYXllciB3b3VsZCBiZSByZXF1aXJlZCwgYXQgd2hpY2ggcG9pbnQgeW91IG5lZWQgdG8gZGVjaWRlLCB3aWxsIHlvdSBzZXQgeW91ciBndWlkZSBmb3JtYXQgYWxsIGluIHRoaXMgbGF5ZXIgb3Igd29yayB3aXRoIGEgc2VwYXJhdGUgKipndWlkZXMoKSoqIGxheWVyPyBEZXBlbmRpbmcgb24gdGhlIGNvbXBsZXhpdHkgb2YgeW91ciBndWlkZXMgKGFzIHdlJ2xsIHJldmlzaXQgbGF0ZXIpIGl0IG1heSBiZSBlYXNpZXIgdG8ga2VlcCB0aGVtIGNlbnRyYWxpemVkLiBJbiBvdGhlciBjYXNlcywgeW91IG1heSB3YW50IHRvIHNldCB0aGVtIHdpdGhpbiB0aGVpciBvd24gKipzY2FsZVxfXCooKSoqIGxheWVycyBpbiBjYXNlIHlvdSB3YW50IHRvIG1ha2UgY2hhbmdlcyB0byBzcGVjaWZpYyBsYXllciBkZXRhaWxzIG1vcmUgY2VudHJhbGl6ZWQuIEl0J3MgYSBiYWxhbmNlIHRoYXQgd2lsbCBiZSBzdHJ1Y2sgYmV0d2VlbiB5b3VyIHNwZWNpZmljIG5lZWRzIGJ1dCB0cnkgdG8gYmUgdGhvdWdodGZ1bCBhYm91dCBpdCB0byBzYXZlIHlvdXJzZWxmIHNvbWUgcGFpbiBpbiBlZGl0aW5nIHlvdXIgY29kZSBsYXRlciBvbi4NCjo6Og0KDQo6Ojoge2FsaWduPSJjZW50ZXIifQ0KPGltZyBzcmM9Imh0dHBzOi8vZ2l0aHViLmNvbS9jYW1vay9DU0JfQ291cnNlX01hdGVyaWFscy9ibG9iL21haW4vQWR2Vml6L2NvbG91cl92aXN1YWxpemF0aW9ucy5qcGc/cmF3PXRydWUiIHdpZHRoPSI4MDAiLz4NCg0KV2VsbCB5b3UgY291bGQgcmVseSBvbiB0aGUgYmFzaWMgY29sb3VyIHBhbGV0dGUgYnV0IHlvdSdyZSBiZXR0ZXIgb2ZmIHBpY2tpbmcgeW91ciBvd24gY29sb3VycyENCjo6Og0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyAzLjAuMCBDb2xvdXIgcGFsZXR0ZXMhDQoNClVwIHRvIHRoaXMgcG9pbnQsIHdlJ3ZlIGRhbmNlZCBhcm91bmQgdGhlIGlkZWEgb2YgY29sb3VyIGluIG91ciBsZWN0dXJlcyBhbmQgYXNzaWdubWVudHMuIEZvciB0aG9zZSBvZiB5b3UgdGhhdCBhcmVuJ3QgZmFtaWxpYXIgd2l0aCB5b3VyIGNvbG91ciBjaG9pY2VzLCBoZXJlIGlzIGEgcXVpY2sgYnJlYWtkb3duIG9mIGNvbG91ciBwYWxldHRlcy4NCg0KQSBjb21tb24gdGhpbmcgdG8gd2FudCB0byBkbyBpcyB0byBjaGFuZ2UgY29sb3VycyBmcm9tIGBnZ3Bsb3QyYCdzIGRlZmF1bHQgcmFpbmJvdyBwYWxldHRlLiBUaGVyZSBhcmUgbWFueSByZWFzb25zIHRvIGNoYW5nZSBhIGNvbG91ciBwYWxldHRlIGluY2x1ZGluZw0KDQotICAgbWFraW5nIGl0IGVhc2llciBvbiB0aGUgcmVhZGVyJ3MgZXllLg0KLSAgIG1ha2luZyBpdCBjb2xvdXItYmxpbmQgZnJpZW5kbHkuDQotICAgZW5zdXJpbmcgdGhhdCBwbG90cyB3aXRoIGNvbnRpbnVvdXMgZGF0YSB1c2UgZ29vZCBjb2xvdXIgc3BlY3RyYSBmb3IgYWNjdXJhdGUgcmVwcmVzZW50YXRpb24uDQoNCldoZW4gd2UgdGFsayBhYm91dCBjb2xvdXIgcGFsZXR0ZXMgYW5kIHRoZWlyIHB1cnBvc2UsIHRoZXJlIGFyZSAzIG1haW4gdHlwZXMuDQoNCiMjIyAzLjAuMSBVc2Ugc2VxdWVudGlhbCBjb2xvdXIgcGFsZXR0ZXMgdG8gZGlzcGxheSBsb3cgdG8gaGlnaCB2YWx1ZXMNCg0KKlNlcXVlbnRpYWwqIC0gaW1wbGllcyBhbiBvcmRlciB0byB5b3VyIGRhdGEgLSBpLmUuIGxpZ2h0IHRvIGRhcmsgaW1wbGllcyBsb3cgdmFsdWVzIHRvIGhpZ2ggdmFsdWVzLiBUaGVyZSBhcmUgaGVscGZ1bCB3aGVuIHdvcmtpbmcgd2l0aCBjb250aW51b3VzIGRhdGEgc2NhbGVzIG9mIGluY3JlYXNpbmcgdmFsdWUgZS5nLiBoZWF0bWFwcy4NCg0KYGBge3J9DQojIExvYWQgdGhlIFJDb2xvckJyZXdlciBsaWJyYXJ5DQpsaWJyYXJ5KFJDb2xvckJyZXdlcikNCg0KIyBkaXNwbGF5IHRoZSBzZXF1ZW50aWFsIGNvbG91ciBwYWxldHRlcw0KZGlzcGxheS5icmV3ZXIuYWxsKHR5cGUgPSAic2VxIikNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDMuMC4yIFVzZSBkaXZlcmdpbmcgY29sb3VyIHBhbGV0dGVzIHRvIGhpZ2hsaWdodCB0aGUgbWlkZGxlIGFuZCBleHRyZW1lcyBvZiBhIGRpc3RyaWJ1dGlvbg0KDQoqRGl2ZXJnaW5nKiAtIGxvdyBhbmQgaGlnaCB2YWx1ZXMgYXJlIGV4dHJlbWVzLCBhbmQgdGhlIG1pZGRsZSB2YWx1ZXMgYXJlIGltcG9ydGFudC4gVGhpcyBwYWxldHRlIHdpbGwgZ29lcyBmcm9tIGxpZ2h0IHRvIGRhcmssIG1pZGRsZSB0byBvdXRzaWRlcyB3aXRoIDMgY29sb3VycyBtYWlubHkgdXNlZC4NCg0KYGBge3J9DQojIERpc3BsYXkgdGhlIGRpdmVyZ2luZyBjb2xvdXIgcGFsZXR0ZXMNCmRpc3BsYXkuYnJld2VyLmFsbCh0eXBlID0gImRpdiIpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAzLjAuMyBVc2UgcXVhbGl0YXRpdmUgY29sb3VyIHBhbGV0dGVzIGZvciBjYXRlZ29yaWNhbCBkYXRhDQoNCipRdWFsaXRhdGl2ZSogLSB0aGVyZSBpcyBubyBxdWFudGl0YXRpdmUgcmVsYXRpb25zaGlwIGJldHdlZW4gY29sb3Vycy4gVGhpcyBpcyB1c3VhbGx5IHVzZWQgZm9yIGNhdGVnb3JpY2FsIGRhdGEgd2hlbiB5b3Ugd2FudCBlYWNoIGNhdGVnb3J5IHRvIGJlIHZpc3VhbGl6ZWQgZGlzdGluY3RseS4NCg0KYGBge3J9DQpkaXNwbGF5LmJyZXdlci5hbGwodHlwZSA9ICJxdWFsIikNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgMy4xLjAgQWRkIGEgY29sb3VyIHBhbGV0dGUgdG8gYSBwbG90IGxpa2UgYSBsYXllcg0KDQpMZXQncyB0ZXN0IG9uZSBvZiB0aGUgYFJDb2xvckJyZXdlcmAgcGFsZXR0ZXMgb3V0IG9uIG91ciBkYXRhLiBXZSdsbCBhZGQgaXQgYXMgYSBsYXllciB0byBgcGh1X3dpbmRvdy5wbG90YCB1c2luZyBgc2NhbGVfY29sb3VyX2JyZXdlcigpYCB0byBvdmVycmlkZSB0aGUgY29sb3VyIG1hcHBpbmdzIGRlZmluZWQgaW4gdGhlIGBhZXMoKWAgbGF5ZXIgb2YgdGhlIHBsb3QuIFNvbWUgcGFyYW1ldGVycyB3ZSBjYW4ga2VlcCBpbiBtaW5kOg0KDQotICAgYHR5cGVgOiBkZXRlcm1pbmVzIHRoZSBraW5kIG9mIHBhbGV0dGUgYXMgc2VxdWVudGlhbCAoc2VxKSwgZGl2ZXJnaW5nIChkaXYpIG9yIHF1YWxpdGF0aXZlIChxdWFsKQ0KDQotICAgYHBhbGV0dGVgOiBhY2NlcHRzIGEgc3RyaW5nIG5hbWUgZm9yIGEgcGFsZXR0ZSBvciBhbiBpbnRlZ2VyIHRoYXQgY29tYmluZXMgd2l0aCBgdHlwZWAgdG8gcGljayBhIHBhbGV0dGUNCg0KTm90ZSB0aGF0IGNvbG91ciBwYWxldHRlcyAqKiphcmUgbm90KioqIHZlY3RvciByZWN5Y2xlZCB3aGVuIHBsb3R0aW5nIGluIGBnZ3Bsb3RgLiBUaGlzIG1lYW5zIGlmIHlvdSBkb24ndCBzdXBwbHkgZW5vdWdoIGNvbG91cnMgdG8gbWF0Y2ggeW91ciBncm91cHMsIHRoZW4gdW5hc3NpZ25lZCBncm91cHMgd2lsbCBzaW1wbHkgYmUgY3V0IG9mZiBvciBub3QgZGlzcGxheWVkLg0KDQpNb3JlIGluZm9ybWF0aW9uIG9uIHBhbGV0dGUgb3JkZXIgYW5kIG90aGVyIHBhcmFtZXRlcnMgY2FuIGJlIGZvdW5kIFtoZXJlXShodHRwczovL2dncGxvdDIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2Uvc2NhbGVfYnJld2VyLmh0bWwpDQoNCmBgYHtyLCBmaWcud2lkdGg9MjAsIGZpZy5oZWlnaHQ9MTB9DQpwaHVfd2luZG93LnBsb3QgKyANCiAgIyBVc2UgdGhlIERhcmsyIHBhbGV0dGUNCiAgc2NhbGVfY29sb3VyX2JyZXdlcihwYWxldHRlPS4uLikNCmBgYA0KDQpgYGB7ciwgZmlnLndpZHRoPTIwLCBmaWcuaGVpZ2h0PTEwfQ0KcGh1X3dpbmRvdy5wbG90ICsgDQogICMgUGljayBhIHF1YWxpdGF0aXZlIGNvbG91ciBwYWxldHRlDQogIHNjYWxlX2NvbG91cl9icmV3ZXIodHlwZT0uLi4sIHBhbGV0dGU9Li4uKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyAzLjIuMCBZb3UgY2FuIGFsd2F5cyBwaWNrIHlvdXIgb3duIGNvbG91cnMhDQoNCllvdSBjYW4gYWx3YXlzIGNob29zZSBhIHZlY3RvciBvZiB5b3VyIG93biBjb2xvcnMgdXNpbmcgdGhpcyAnUiBjb2xvciBjaGVhdHNoZWV0JyAoPGh0dHBzOi8vd3d3Lm5jZWFzLnVjc2IuZWR1L35mcmF6aWVyL1JTcGF0aWFsR3VpZGVzL2NvbG9yUGFsZXR0ZUNoZWF0c2hlZXQucGRmPikuDQoNCk5hbWVzIG9mIGNvbG91cnMgYXMgd2VsbCBhcyBoZXggY29sb3VyIGNvZGVzIGFyZSBhY2NlcHRlZC4gWW91IGNhbiBzdXBwbHkgYSBtYW51YWwgbGlzdCB1c2luZyB0aGUgYHNjYWxlXypfbWFudWFsKClgIGNvbW1hbmQuDQoNCmBgYHtyLCBmaWcud2lkdGg9MjAsIGZpZy5oZWlnaHQ9MTB9DQpwaHVfd2luZG93LnBsb3QgKyANCiAgICAjIFNldCB5b3VyIG93biBtYW51YWwgY29sb3VyIGNob2ljZXMNCiAgICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcz1jKC4uLiwgImNvcm5mbG93ZXJibHVlIiwgIm9yYW5nZSIsIC4uLikpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDMuMy4wIENvbG91ci1ibGluZCBmcmllbmRseSBwYWxldHRlcyBjYW4gYmUgZm91bmQgaW4gdGhlIGB2aXJpZGlzYCBwYWNrYWdlDQoNClRoZSBgdmlyaWRpc2AgcGFja2FnZSBhbHNvIGhhcyBzb21lIG5pY2UgY29sb3IgcGFsZXR0ZXMgKDxodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvdmlyaWRpcy92aWduZXR0ZXMvaW50cm8tdG8tdmlyaWRpcy5odG1sPikuIFRoZXNlIGNvbG91ciBwYWNrYWdlcyBhcmUgZGl2ZXJnaW5nIHBhbGV0dGVzIG1lYW50IHRvIGhlbHAgaGlnaGxpZ2h0IHRydWUgY29sb3VyIGNoYW5nZSBhY3Jvc3MgY29udGludW91cyBzY2FsZXMuIFlvdSd2ZSBzZWVuIGl0IGNvbWUgdXAgYSBmZXcgdGltZXMgaW4gb3VyIGRhdGEgYW5kIHRoZXNlIHBhbGV0dGVzIGRvIHdlbGwgZm9yIHNtYWxsIGNhdGVnb3JpY2FsIHNldHMgYnV0IGJlZ2luIHRvIGJsZW5kIGFzIG91ciBudW1iZXIgb2YgY2F0ZWdvcmllcyBpbmNyZWFzZSBpbiBzaXplLg0KDQpUaGUgbWFpbiBjYWxscyB3ZSBjYW4gdXNlIGZvbGxvdyB0aGUgZm9ybWF0IGBzY2FsZV8qX3ZpcmlkaXNfYy9kL2IoKWAgd2hlcmUgdGhlICJjL2QvYiIgcmVwcmVzZW50cyBjb250aW51b3VzL2Rpc2NyZXRlL2Jpbm5lZCBkYXRhIGFuZCB0aGUgdHlwZXMgb2YgYWRkaXRpb25hbCBhcmd1bWVudHMgdGhhdCBjYW4gYmUgcGFzc2VkIG9uIHRvIGF1Z21lbnQgdGhlIGNhbGwuIFRoZXJlIGFyZSBzb21lIGFkZGl0aW9uYWwgcGFyYW1ldGVycyB0aGF0IGNhbiBiZSB1c2VkIHRvIHNldCB0aGUgY29sb3VycyB3aGVuIGNhbGxlZDoNCg0KLSAgIGBvcHRpb25gOiBhY2NlcHRzIG9uZSBvZiA4IHBvc3NpYmxlIGNoYXJhY3RlciByZXByZXNlbnRpbmcgNSBjb2xvdXIgc2NhbGVzOyAibWFnbWEiLyJBIiwgImluZmVybm8iLyJCIiwgInBsYXNtYSIvIkMiLCAidmlyaWRpcyIvIkQiIG9yICJjaXZpZGlzIi8iRSIuDQoNCi0gICBgZGlyZWN0aW9uYDogc2V0cyB0aGUgZGlyZWN0aW9uIG9mIHRoZSBwYWxldHRlIG9yZGVyLiBVc2UgLTEgdG8gcmV2ZXJzZSBpdC4NCg0KYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD0xMH0NCnBodV93aW5kb3cucGxvdCArIA0KICAgICMgVXNlIGEgY29sb3VyLWJsaW5kIGZyaWVuZGx5IHBhbGV0dGUNCiAgICBzY2FsZV9jb2xvdXJfdmlyaWRpc19kKC4uLikNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgMy40LjAgVXNlIGBhZnRlcl9zY2FsZWAgdG8gc2V0IGFuIGFlc3RoZXRpYyBtYXBwaW5nIGRlcGVuZGVudCB1cG9uIGFub3RoZXIgb25lDQoNClRoZXJlIG1heSBiZSB0aW1lcyB3aGVuIHlvdSB3YW50IHRvIGxpbmsgY2VydGFpbiBhZXN0aGV0aWNzIHRvIGVhY2ggb3RoZXIgbGlrZSBgY29sb3VyYCBhbmQgYGZpbGxgIGZvciBpbnN0YW5jZS4gUGVyaGFwcyB5b3Ugd2FudCB0byBzZXQgYm90aCB0byBhIGN1c3RvbSB2YWx1ZSBidXQgb25lIGFzIGEgbGlnaHRlciBzaGFkZS4gUmF0aGVyIHRoYW4gc2V0IGJvdGggbWFwcGluZ3MgdG8gYSBkYXRhIHZhcmlhYmxlICphbmQgdGhlbiogdXNpbmcgYSBzY2FsZSBsYXllciB0byBzZXQgdGhlIHZhbHVlcywgeW91IGNhbiBzZXQgb25lIG1hcHBpbmcgYXMgZGVwZW5kZW50IHVwb24gYW5vdGhlci4gVGhlcmUgYXJlIHRyYW5zZm9ybWF0aW9ucyBhbmQgbWFwcGluZ3Mgb2YgZGF0YSB0byBhZXN0aGV0aWNzIGhhcHBlbmluZyB1bmRlciB0aGUgaG9vZCBhdCAzIHN0YWdlcyB3aGVuIGV2YWx1YXRpbmcgYSBgZ2dwbG90YCBvYmplY3QuDQoNCjEuICBUaGUgZGVmYXVsdCBzdGFnZSB0YWtlcyBwbGFjZSB3aGVuIHlvdSBmaXJzdCB1c2UgdGhlIGFlcygpIGxheWVyIHRvIG1hcCB0aGUgZGF0YSBsYXllciBkaXJlY3RseSB0byBhbiBhZXN0aGV0aWMuDQoyLiAgVGhlIHNlY29uZCBzdGFnZSBvY2N1cnMgYWZ0ZXIgZGF0YSBoYXMgYmVlbiB0cmFuc2Zvcm1lZCBieSB0aGUgc3RhdCgpIGxheWVyLiBUaGlzIG9jY3VycyBpbiBwbGFjZXMgd2hlcmUgZGF0YSBpcyBzdW1tYXJpemVkLCBzdWNoIGFzIGEgZ2VvbV9iYXIoKSBhcyB0aGUgZGF0YSBpc24ndCBkaXJlY3RseSBiZWluZyB1c2VkIGJ1dCByYXRoZXIgc3VtbWFyaXplZCBpbnRvIGEgc2V0IG9mIGRhdGEgZm9yIGRpc3BsYXkuIFlvdSB3b3VsZCB1c2UgYGFmdGVyX3N0YXQoKWAgdG8gYWNjZXNzIHRoaXMgZGF0YS4NCjMuICBUaGUgZmluYWwgc3RhZ2UgaXMgYWZ0ZXIgdGhlIGRhdGEgaGFzIGJlZW4gdHJhbnNmb3JtZWQgYW5kIG1hcHBlZCBieSB0aGUgcGxvdCBzY2FsZXMgKGVnIGBzY2FsZV9jb2xvdXJfbWFudWFsKClgKS4gRnJvbSB0aGVyZSwgeW91IGNhbiBkaWN0YXRlIGhvdyBhbm90aGVyIGFlc3RoZXRpYyBtYXBwaW5nIHdpbGwgZGV0ZXJtaW5lIGl0cyB2YWx1ZXMuDQoNClVzaW5nIHRoZSBgYWZ0ZXJfc2NhbGUoKWAgZnVuY3Rpb24gd2lsbCBwb3N0cG9uZSBhbiBhZXN0aGV0aWMgbWFwcGluZyB1bnRpbCBhZnRlciB0aGUgZGF0YSBoYXMgYmVlbiBzY2FsZWQuIEFzIHdlJ2xsIHNlZSBuZXh0LCB3aGVuIHVzZWQgcHJvcGVybHksIHlvdSB3aWxsIHRpZSB0aGUgYWVzdGhldGljcyBvZiBvbmUgYXNwZWN0IHRvIHRoZSBhZXN0aGV0aWNzIG9mIGFub3RoZXIuIFRoZXJlIGFyZSBhIG51bWJlciBvZiBjb29sIHdheXMgeW91IGNhbiB1dGlsaXplIGBhZnRlcl9zdGF0KClgIGFzIHdlbGwgdG8gW2FkZCBmaW5pc2hpbmcgdG91Y2hlcyBsaWtlIGNvdW50cy92YWx1ZXMgdG8geW91ciBncmFwaHNdKGh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9hZXNfZXZhbC5odG1sKS4gVGhlIGBhZnRlcl9zY2FsZSgpYCBmZWF0dXJlIHdpbGwgYWxzbyBzaW1wbGlmeSBvdXIgY29kZSBzbyB0aGF0IGlmIHdlIHdhbnQgdG8gY2hhbmdlIG9uZSBhc3BlY3QsIHRoZW4gYWxsIGRlcGVuZGVudCBhc3BlY3RzIHdpbGwgY2hhbmdlIHdpdGggaXQuDQoNCkdvaW5nIGJhY2sgdG8gb3VyIHByZXZpb3VzIGJveHBsb3QsIHdlJ2xsIHV0aWxpemUgYGFmdGVyX3NjYWxlKClgIHRvIGxpbmsgdGhlIGZpbGwgdmFsdWVzIG9mIG91ciB2aW9saW4gcGxvdCAqKip0byoqKiB0aGUgY29sb3VyIHNldCBvZiB0aGUgc2FtZSB2aW9saW4gcGxvdC4gQXQgdGhlIHNhbWUgdGltZSB3ZSdsbCBkZS1jb3VwbGUgdGhvc2UgYWVzdGhldGljcyBmcm9tIHRoZSBvbmVzIHdlIHVzZSBpbiB0aGUgaW5zZXQgYm94cGxvdCBvZiB0aGUgdmlzdWFsaXphdGlvbnMuIEVub3VnaCB0YWxrIHRob3VnaCwgbGV0J3Mgc2VlIHdoYXQgdGhhdCBsb29rcyBsaWtlLg0KDQpgYGB7ciwgZmlnLndpZHRoPTIwLCBmaWcuaGVpZ2h0PTEwfQ0KDQojIEJ1aWxkIGFuZCBzYXZlIHRoZSBwbG90IGZvciBsYXRlciB1c2UNCmRlbW9ncmFwaGljcy5wbG90IDwtIGNvdmlkX2RlbW9ncmFwaGljc190b3RhbC5kZiAlPiUgDQogICMgVW5ncm91cCB0aGlzIGRhdGFmcmFtZSB0byBjbGVhbiBpdCB1cCBhIGxpdHRsZQ0KICB1bmdyb3VwKCkgJT4lIA0KICAjIEZpbHRlciBmb3IgY3VtdWxhdGl2ZSBkYXRhDQogIGZpbHRlcihwZXJpb2QgPT0gImN1bXVsYXRpdmUiKSAlPiUgDQogICMgU2VsZWN0IGZvciBqdXN0IHRoZSBpbXBvcnRhbnQgY29sdW1ucw0KICBzZWxlY3QocHVibGljX2hlYWx0aF91bml0LCBhZ2VfZ3JvdXAsIHBlcmNlbnRfY2FzZXMsIHBlcmNlbnRfaG9zcGl0YWxpemF0aW9ucykgJT4lIA0KICAjIFBpdm90IHRoZSBtb2RpZmllZCB0YWJsZSB0byBjYXB0dXJlIHRoZSAic3RhdF9ncm91cCIgb2YgcGVyY2VudF9jYXNlcyB2cyBwZXJjZW50X2hvc3BpdGFsaXphdGlvbnMNCiAgcGl2b3RfbG9uZ2VyKGNvbHM9YygzLDQpLCBuYW1lc190byA9ICJzdGF0X2dyb3VwIiwgdmFsdWVzX3RvID0gInBlcmNlbnRfUEhVX3RvdGFsIikgJT4lIA0KICANCiAgIyBQbG90IHRoZSBkYXRhIGFzIGEgZ3JvdXBlZCB2aW9saW4gcGxvdCB3aXRoIGluc2V0IGJveHBsb3QNCiAgDQogICMgMS4gRGF0YQ0KICBnZ3Bsb3QoLikgKw0KICAgICMgMi4gQWVzdGhldGljcw0KICAgIGFlcyh4PWFnZV9ncm91cCwgeSA9IHBlcmNlbnRfUEhVX3RvdGFsKSArDQoNCiAgICAjIFN0YXJ0IHdpdGggYSBiYXNlIHRoZW1lDQogICAgdGhlbWVfbWluaW1hbCgpICsNCg0KICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0yMCksICMgc2V0IHRleHQgc2l6ZSB0byAyMA0KICAgICAgICAgIA0KICAgICAgICAgICMgTW92ZSB0aGUgbGVnZW5kIGFyb3VuZCB0byB3aXRoaW4gdGhlIHBhbmVsIHNwYWNlDQogICAgICAgICAgbGVnZW5kLmp1c3RpZmljYXRpb24gPSBjKDAsMSksDQogICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gYygwLjAyLDAuOTUpLA0KICAgICAgICAgIGxlZ2VuZC5kaXJlY3Rpb24gPSAiaG9yaXpvbnRhbCIsIA0KICAgICAgICAgIA0KICAgICAgICAgICMgVXBkYXRlIHRoZSBwYW5lbCB0byBkcm9wIHRoZSBtaW5vciBheGlzIGdyaWQgbGluZXMNCiAgICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgIA0KICAgICAgICAgICMgVXNlIGEgYmxhY2sgbGluZSBmb3IgdGhlIGF4ZXMNCiAgICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiksDQogICAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KGNvbG91ciA9ICJibGFjayIsIGZhY2U9ImJvbGQiKSwNCiAgICAgICAgICkgKw0KICAgIA0KICAgICMgQWRkIGxhYmVscyB0byB0aGUgcGxvdA0KICAgIGxhYnModGl0bGUgPSAiUGVyY2VudCBjYXNlcyBhbmQgaG9zcGl0YWxpemF0aW9ucyBieSBwcm9wb3J0aW9uIHBlciBQSFUgYWNyb3NzIGFnZSBncm91cCIsDQogICAgICAgICB4ID0gIlxuQWdlIGdyb3VwIiwNCiAgICAgICAgIHkgPSAiUHJvcG9ydGlvbiBvZiByZXBvcnRlZCBQSFUgZGF0YVxuIiwNCiAgICAgICAgIGNhcHRpb24gPSAiXG4qQWdlIGdyb3VwIHZhbHVlcyBhcmUgY2FsY3VsYXRlZCBhcyBhIHBlcmNlbnRhZ2Ugb2YgdG90YWwgY2FzZXMgb3IgaG9zcGl0YWxpemF0aW9ucyB3aXRoaW4gYSBQSFUiKSArDQoNCiAgICAjIyMgVXNlIHRoZSBndWlkZXMoKSBsYXllciBhbmQgZ2V0IHJpZCBvZiB0aGUgc2NhbGVfZmlsbF9kaXNjcmV0ZSgpIGxheWVyDQogICAgZ3VpZGVzKGZpbGwgPSAibm9uZSIsIGdyb3VwID0gIm5vbmUiKSArDQoNCiAgICAjIDMuIFNjYWxpbmcNCiAgICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygwLCAwLjUpKSArICAgICAgICAgIyBTZXQgdGhlIGxpbWl0cyBvZiBvdXIgeS1heGlzIA0KDQogICAgIyBTZXQgdGhlIGxhYmVscyBvZiBvdXIgeC1heGlzIGNhdGVnb3JpZXMNCiAgICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscz1jKCIwLTQiLCAiNS0xMSIsICIxMi0xOSIsICIyMC0zOSIsICI0MC01OSIsICI2MC03OSIsICI4MCsiKSkgKw0KDQogICAgIyBTZXQgdGhlIGNvbG91ciBsZWdlbmQgDQogICAgc2NhbGVfY29sb3VyX2Rpc2NyZXRlKG5hbWUgPSAiRGF0YSBjYXRlZ29yeSIsIGxhYmVscyA9IGMoIiUgY2FzZXMiLCAiJSBob3NwaXRhbGl6YXRpb25zIikpICsNCg0KICAgICMgNC4gRGF0YQ0KICAgICMgbXVsdGktZmFjdG9yIHZpb2xpbiBwbG90cyBidXQga2VlcCB0aGUgd2lkdGggY29uc2lzdGVudA0KDQogICAgIyMjIDMuNC4wIExpbmsgeW91ciBmaWxsIHRvIHRoZSBjb2xvdXIgYWVzdGhldGljDQogICAgZ2VvbV92aW9saW4oc2NhbGU9IndpZHRoIiwgDQogICAgICAgICAgICAgICAgYWVzKGNvbG91ciA9IHN0YXRfZ3JvdXAsIGZpbGw9Li4uKSwgDQogICAgICAgICAgICAgICAgbHdkID0gMS41KSArIA0KDQogICAgIyBCb3hwbG90IGJ1dCBzbWFsbGVyIHdpZHRoIHNvIHRoZXkgcmVzaWRlICJ3aXRoaW4iIHRoZSB2aW9saW4gcGxvdA0KICAgIGdlb21fYm94cGxvdChhZXMoZmlsbCA9IHN0YXRfZ3JvdXApLCB3aWR0aD0wLjIsIA0KICAgICAgICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoPTAuOSksIA0KICAgICAgICAgICAgICAgICBvdXRsaWVyLnNoYXBlPU5BKSArICMgUmVtb3ZlIHRoZSBvdXRsaWVycw0KDQogICAgIyBBZGQgaW4gYWxsIG9mIHRoZSBkYXRhIHBvaW50cw0KICAgIGdlb21fcXVhc2lyYW5kb20oZG9kZ2Uud2lkdGggPSAwLjg1LCBhZXMoZ3JvdXA9c3RhdF9ncm91cCksIGFscGhhID0gMC44KQ0KDQojIFNob3cgdGhlIHBsb3QNCmRlbW9ncmFwaGljcy5wbG90DQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCjo6OiB7LmFsZXJ0IC5hbGVydC1ibG9jayAuYWxlcnQtZGFuZ2VyfQ0KKipTZWN0aW9uIDMuMC4wIENvbXByZWhlbnNpb24gUXVlc3Rpb24qKiBBcmUgeW91IGNvbnZpbmNlZCBvZiB0aGUgYmVuZWZpdHMgb3IgZGlmZmVyZW5jZXMgaW4gdXNpbmcgKiphZnRlcl9zY2FsZSgpKio/IFBsYXkgd2l0aCB0aGUgY29kZSBiZWxvdyBhbmQgc2VlIHdoYXQgaGFwcGVucyB3aGVuIHlvdSB1c2UgKipzY2FsZV9maWxsX21hbnVhbCgpKiogb3IgKipzY2FsZV9jb2xvdXJfbWFudWFsKCkqKiB0byBzZXQgZGlmZmVyZW50IHZhbHVlcyBmb3IgeW91ciBmaWxsIHZzIGNvbG91ciBhZXN0aGV0aWNzPyBXaGF0IGlzIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gc2V0dGluZyB0aGVzZSB0d28gbGF5ZXJzIGluIHRoZSBjb250ZXh0IG9mIHVzaW5nICoqYWZ0ZXJfc2NhbGUoKSoqPw0KOjo6DQoNCmBgYHtyLCBmaWcud2lkdGg9MjAsIGZpZy5oZWlnaHQ9MTIsIGVycm9yPVRSVUV9DQoNCmNvdmlkX2RlbW9ncmFwaGljc190b3RhbC5kZiAlPiUgDQogICMgVW5ncm91cCB0aGlzIGRhdGFmcmFtZSB0byBjbGVhbiBpdCB1cCBhIGxpdHRsZQ0KICB1bmdyb3VwKCkgJT4lIA0KICAjIEZpbHRlciBmb3IgY3VtdWxhdGl2ZSBkYXRhDQogIGZpbHRlcihwZXJpb2QgPT0gImN1bXVsYXRpdmUiKSAlPiUgDQogICMgU2VsZWN0IGZvciBqdXN0IHRoZSBpbXBvcnRhbnQgY29sdW1ucw0KICBzZWxlY3QocHVibGljX2hlYWx0aF91bml0LCBhZ2VfZ3JvdXAsIHBlcmNlbnRfY2FzZXMsIHBlcmNlbnRfaG9zcGl0YWxpemF0aW9ucykgJT4lIA0KICAjIFBpdm90IHRoZSBtb2RpZmllZCB0YWJsZSB0byBjYXB0dXJlIHRoZSAic3RhdF9ncm91cCIgb2YgcGVyY2VudF9jYXNlcyB2cyBwZXJjZW50X2hvc3BpdGFsaXphdGlvbnMNCiAgcGl2b3RfbG9uZ2VyKGNvbHM9YygzLDQpLCBuYW1lc190byA9ICJzdGF0X2dyb3VwIiwgdmFsdWVzX3RvID0gInBlcmNlbnRfUEhVX3RvdGFsIikgJT4lIA0KICANCiAgIyBQbG90IHRoZSBkYXRhIGFzIGEgZ3JvdXBlZCB2aW9saW4gcGxvdCB3aXRoIGluc2V0IGJveHBsb3QNCiAgDQogICMgMS4gRGF0YQ0KICBnZ3Bsb3QoLikgKw0KICAgICMgMi4gQWVzdGhldGljcw0KICAgIGFlcyh4PWFnZV9ncm91cCwgeSA9IHBlcmNlbnRfUEhVX3RvdGFsKSArDQoNCiAgICAjIFN0YXJ0IHdpdGggYSBiYXNlIHRoZW1lDQogICAgdGhlbWVfbWluaW1hbCgpICsNCg0KICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0yMCksICMgc2V0IHRleHQgc2l6ZSB0byAyMA0KICAgICAgICAgIA0KICAgICAgICAgICMgTW92ZSB0aGUgbGVnZW5kIGFyb3VuZCB0byB3aXRoaW4gdGhlIHBhbmVsIHNwYWNlDQogICAgICAgICAgbGVnZW5kLmp1c3RpZmljYXRpb24gPSBjKDAsMSksDQogICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gYygwLjAyLDAuOTUpLA0KICAgICAgICAgIGxlZ2VuZC5kaXJlY3Rpb24gPSAiaG9yaXpvbnRhbCIsIA0KICAgICAgICAgIA0KICAgICAgICAgICMgVXBkYXRlIHRoZSBwYW5lbCB0byBkcm9wIHRoZSBtaW5vciBheGlzIGdyaWQgbGluZXMNCiAgICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgIA0KICAgICAgICAgICMgVXNlIGEgYmxhY2sgbGluZSBmb3IgdGhlIGF4ZXMNCiAgICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiksDQogICAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KGNvbG91ciA9ICJibGFjayIsIGZhY2U9ImJvbGQiKSwNCiAgICAgICAgICkgKw0KICAgIA0KICAgICMgQWRkIGxhYmVscyB0byB0aGUgcGxvdA0KICAgIGxhYnModGl0bGUgPSAiUGVyY2VudCBjYXNlcyBhbmQgZGVhdGhzIGJ5IHByb3BvcnRpb24gcGVyIFBIVSBhY3Jvc3MgYWdlIGdyb3VwIiwNCiAgICAgICAgIHggPSAiXG5BZ2UgZ3JvdXAiLA0KICAgICAgICAgeSA9ICJQcm9wb3J0aW9uIG9mIHJlcG9ydGVkIFBIVSBkYXRhXG4iLA0KICAgICAgICAgY2FwdGlvbiA9ICJcbipBZ2UgZ3JvdXAgdmFsdWVzIGFyZSBjYWxjdWxhdGVkIGFzIGEgcGVyY2VudGFnZSBvZiB0b3RhbCBjYXNlcyBvciBkZWF0aHMgd2l0aGluIGEgUEhVIikgKw0KDQogICAgIyMjIFVzZSB0aGUgZ3VpZGVzKCkgbGF5ZXIgYW5kIGdldCByaWQgb2YgdGhlIHNjYWxlX2ZpbGxfZGlzY3JldGUoKSBsYXllcg0KICAgIGd1aWRlcyhmaWxsID0gIm5vbmUiLCBncm91cCA9ICJub25lIikgKw0KDQogICAgIyAzLiBTY2FsaW5nDQogICAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwgMC41KSkgKyAgICAgICAgICMgU2V0IHRoZSBsaW1pdHMgb2Ygb3VyIHktYXhpcyANCg0KICAgICMgU2V0IHRoZSBsYWJlbHMgb2Ygb3VyIHgtYXhpcyBjYXRlZ29yaWVzDQogICAgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHM9YygiMC00IiwgIjUtMTEiLCAiMTItMTkiLCAiMjAtMzkiLCAiNDAtNTkiLCAiNjAtNzkiLCAiODArIikpICsNCg0KICAgICMgU2V0IHRoZSBjb2xvdXIgbGVnZW5kIA0KICAgICMjIyAzLjAuMCBDb21wcmVoZW5zaW9uIHF1ZXN0aW9uDQogICAgc2NhbGVfY29sb3VyX21hbnVhbChuYW1lID0gIkRhdGEgY2F0ZWdvcnkiLCBsYWJlbHMgPSBjKCIlIGNhc2VzIiwgIiUgaG9zcGl0YWxpemF0aW9ucyIpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWVzID0gYyguLi4pKSArDQoNCiAgICBzY2FsZV9maWxsX21hbnVhbChuYW1lID0gIkRhdGEgY2F0ZWdvcnkiLCBsYWJlbHMgPSBjKCIlIGNhc2VzIiwgIiUgaG9zcGl0YWxpemF0aW9ucyIpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWVzID0gYyguLi4pKSArDQoNCiAgICAjIDQuIERhdGENCiAgICAjIG11bHRpLWZhY3RvciB2aW9saW4gcGxvdHMgYnV0IGtlZXAgdGhlIHdpZHRoIGNvbnNpc3RlbnQNCg0KICAgICMjIyBMaW5rIHlvdXIgZmlsbCB0byB0aGUgY29sb3VyIGFlc3RoZXRpYw0KICAgIGdlb21fdmlvbGluKHNjYWxlPSJ3aWR0aCIsIA0KICAgICAgICAgICAgICAgIGFlcyhjb2xvdXIgPSBzdGF0X2dyb3VwLCBmaWxsPWFmdGVyX3NjYWxlKGFscGhhKGNvbG91ciwgMC4zKSkpLCANCiAgICAgICAgICAgICAgICBsd2QgPSAxLjUpICsgDQoNCiAgICAjIEJveHBsb3QgYnV0IHNtYWxsZXIgd2lkdGggc28gdGhleSByZXNpZGUgIndpdGhpbiIgdGhlIHZpb2xpbiBwbG90DQogICAgZ2VvbV9ib3hwbG90KGFlcyhmaWxsID0gc3RhdF9ncm91cCksIHdpZHRoPTAuMiwgDQogICAgICAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2Uod2lkdGg9MC45KSwgDQogICAgICAgICAgICAgICAgIG91dGxpZXIuc2hhcGU9TkEpICsgIyBSZW1vdmUgdGhlIG91dGxpZXJzDQoNCiAgICAjIEFkZCBpbiBhbGwgb2YgdGhlIGRhdGEgcG9pbnRzDQogICAgZ2VvbV9xdWFzaXJhbmRvbShkb2RnZS53aWR0aCA9IDAuODUsIGFlcyhncm91cD1zdGF0X2dyb3VwKSwgYWxwaGEgPSAwLjgpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCjo6OiB7YWxpZ249ImNlbnRlciJ9DQo8aW1nIHNyYz0iaHR0cHM6Ly9naXRodWIuY29tL2NhbW9rL0NTQl9Db3Vyc2VfTWF0ZXJpYWxzL2Jsb2IvbWFpbi9BZHZWaXovZmluZS10dW5lX3Bsb3RzLnBuZz9yYXc9dHJ1ZSIgd2lkdGg9IjgwMCIvPg0KDQpJdCdzIGFsbCBhYm91dCBmaWd1cmluZyBvdXQgaG93IHRvIGFkZCB0aG9zZSBmaW5pc2hpbmcgdG91Y2hlcw0KOjo6DQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIDQuMC4wIEFubm90YXRpbmcgeW91ciBwbG90cw0KDQpBZnRlciBwcmVwYXJpbmcgeW91ciB2aXN1YWxpemF0aW9uIHlvdSBtYXkgY29uc2lkZXIgYWRkaW5nIGV4dHJhIGFubm90YXRpb25zLiBUaGVzZSBhcmUgKnVzdWFsbHkqIGxheWVycyB0aGF0IGRvbid0IGFmZmVjdCB0aGUgYWVzdGhldGljcyBvciBkYXRhIG9mIHlvdXIgdmlzdWFsaXphdGlvbiBidXQgZGVwZW5kaW5nIG9uIGhvdyB5b3UgYWRkIHRoZW0gYW5kIHRoZSBwYWNrYWdlIHlvdSBhcmUgdXNpbmcgdGhpcyBpc24ndCBzdHJpY3RseSB0cnVlLiBGb3IgdGhlIG1vc3QgcGFydCwgaG93ZXZlciwgbGV0J3MgY29uc2lkZXIgeW91ciBhbm5vdGF0aW9ucyBhcyBzZXBhcmF0ZSBmcm9tIHlvdXIgcGxvdC4NCg0KV2UndmUgYWxyZWFkeSBkYWJibGVkIGluIGFubm90YXRpb25zIHNpbmNlIHRoZSBmaXJzdCBsZWN0dXJlIGJ1dCBub3cgd2UncmUgZ29pbmcgdG8gbG9vayBkZWVwbHkgYXQgaG93IHRoZXNlIHdvcmsgYW5kIHNvbWUgbW9yZSBhZHZhbmNlZCBhbm5vdGF0aW9uIHBhY2thZ2VzLg0KDQojIyA0LjEuMCBgYW5ub3RhdGUoKWAgcGxvdHMgd2l0aCBzaGFwZXMsIHRleHQsIGFuZCBhcnJvd3MuDQoNClNvbWV0aW1lcyB5b3UgbmVlZCB0byBhZGQgc29tZSBhZGRpdGlvbmFsIHRleHQsIG9yIHNoYXBlcyB0byB5b3VyIGdyYXBoIHRoYXQgYXJlbid0IG5lY2Vzc2FyaWx5IGEgcGFydCBvZiB0aGUgZGF0YSBpdHNlbGYuIEluIG90aGVyIHdvcmRzIHlvdSB3b3VsZCBsaWtlIHRvICphbm5vdGF0ZSogeW91ciBwbG90LiBUbyBhY2NvbXBsaXNoIHRoaXMgeW91IGNhbiB1c2UgdGhlIGBhbm5vdGF0ZSgpYCBmdW5jdGlvbiB3aGljaCB3aWxsIGVzc2VudGlhbGx5IGFkZCBnZW9tcyB0byB5b3VyIHBsb3QuIFdoaWxlIHRoZXNlIGFubm90YXRpb25zIGNhbiBhZmZlY3QgdGhlIGF4aXMgbGltaXRzIG9mIHlvdXIgcGxvdCBpZiBpdCBpcyByZXF1aXJlZCB0byBzaG93IHlvdXIgYW5ub3RhdGlvbihzKSwgdGhleSB3b24ndCBhZmZlY3QgdGhlIGxlZ2VuZHMgbm9yIGJlIHRyZWF0ZWQgYXMgYWN0dWFsIGRhdGEgLSBqdXN0IGFuIG92ZXJsYXkgdG8geW91ciBwbG90Lg0KDQpUaGUgYGFubm90YXRlKClgIGdlb20gaGFzIHRoZSBmb2xsb3dpbmcgcGFyYW1ldGVyczoNCg0KfCAgICAgICAgICAgICBQYXJhbWV0ZXIgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRGVzY3JpcHRpb24gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfDotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLTp8Oi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tOnwNCnwgICAgICAgICAgICAgICAgZ2VvbSAgICAgICAgICAgICAgICB8ICBDYW4gYmUgYW55IG51bWJlciBvZiBwb3NzaWJsZSB2YWx1ZXMgaW5jbHVkaW5nICJ0ZXh0IiwgInJlY3QiLCAic2VnbWVudCIsICJjdXJ2ZSIsIGV0Yy4gIHwNCnwgeG1pbiwgeG1heCwgeW1pbiwgeW1heCwgeGVuZCwgeWVuZCB8ICAgICAgICAgICAgUG9zaXRpb25pbmcgYWVzdGhldGljcyB3aGVyZSBhdCBsZWFzdCBvbmUgb2YgdGhlc2UgbXVzdCBiZSBkZWZpbmVkLiAgICAgICAgICAgIHwNCnwgICAgICAgICAgICAgICAgLi4uICAgICAgICAgICAgICAgICB8ICAgICAgICAgT3RoZXIgYWVzdGhldGljcyBhcmd1bWVudHMgdGhhdCBjYW4gYmUgcGFzc2VkIGFsb25nIGxpa2UgYGNvbG9yID0gInJlZCJgICAgICAgICAgIHwNCnwgICAgICAgICAgICAgICBuYS5ybSAgICAgICAgICAgICAgICB8IElmIGBGQUxTRWAsIG1pc3NpbmcgdmFsdWVzIGFyZSByZW1vdmVkIHdpdGggYSB3YXJuaW5nIG90aGVyd2lzZSB0aGV5IGFyZSBzaWxlbnRseSByZW1vdmVkIHwNCg0KVXAgdG8gdGhpcyBwb2ludCB3ZSd2ZSBhbHJlYWR5IGFkZGVkIHNvbWUgYW5ub3RhdGlvbnMgdG8gdGhpcyBwbG90IGluIHByZXZpb3VzIGxlY3R1cmVzLiBUb2RheSB3ZSdsbCB1cGRhdGUgYSBmZXcgYml0cyBvZiB0ZXh0IGFuZCBsaW5lcyBzZWdtZW50cyB3aXRoIGFycm93cyBpbnN0ZWFkIG9mIGJveGVzLg0KDQpXaGVuIG5hbWluZyB5b3VyIGBnZW9tYCBwYXJhbWV0ZXIsIHlvdSBjYW4gZXNzZW50aWFsbHkgdXNlIHdoYXRldmVyIGBnZW9tXyooKWAgYXJlIGF2YWlsYWJsZSB3aXRoaW4gZ2dwbG90LiBGb3IgaW5zdGFuY2UsIHdlJ2xsIGFubm90YXRlIHVzaW5nIGEgYGdlb21fY3VydmUoKWAgYnkgc2V0dGluZyBgZ2VvbSA9ICJjdXJ2ZSJgLiBTb21lIG9mIHRoZSBgZ2VvbV9jdXJ2ZSgpYCBwYXJhbWV0ZXJzIGluY2x1ZGU6DQoNCi0gICBgeGAsIGB4ZW5kYCwgYHlgLCBgeWVuZGA6IHRoZSBzdGFydCBhbmQgZW5kIGNvb3JkaW5hdGVzIG9mIHlvdXIgY3VydmUuDQoNCi0gICBgbGluZWVuZGA6IHRoZSBsaW5lIGVuZCBzdHlsZSAocm91bmQsIGJ1dHQsIHNxdWFyZSkuDQoNCi0gICBgY3VydmF0dXJlYDogYW4gaW50ZWdlciBkZXNjcmliaW5nIHRoZSB0eXBlIG9mIGN1cnZhdHVyZSBqb2luaW5nIHN0YXJ0IHRvIGVuZC4NCg0KICAgIC0gICBOZWdhdGl2ZSB2YWx1ZXMgcHJvZHVjZSBhIGxlZnQtaGFuZCBjdXJ2ZS4NCg0KICAgIC0gICBQb3NpdGl2ZSB2YWx1ZXMgcHJvZHVjZSBhIHJpZ2h0LWhhbmQgY3VydmUuDQoNCiAgICAtICAgMCBwcm9kdWNlcyBhIHN0cmFpZ2h0IGxpbmUuDQoNCi0gICBgYW5nbGVgOiBhbiBhbW91bnQgKDAgdG8gMTgwKSB0byBza2V3IHRoZSBjb250cm9sIHBvaW50cyBvZiB0aGUgY3VydmUuDQoNCmBgYHtyLCBmaWcud2lkdGg9MjAsIGZpZy5oZWlnaHQ9MTJ9DQoNCiMgVXBkYXRlIG91ciBwaHVfd2luZG93LnBsb3Qgd2l0aCBzb21lIGFubm90YXRpb25zIGFuZCBzYXZlIGl0IHRvIGEgbmV3IG9iamVjdA0KcGh1X3dpbmRvd19hbm5vdGF0ZS5wbG90IDwtDQogIHBodV93aW5kb3cucGxvdCArIA0KICAgICMgMi4gQWVzdGhldGljcw0KICAgICMgTW92ZSBvdXIgbGVnZW5kIHRvIHRoZSByaWdodCBzaWRlIG9mIHRoZSBwYW5lbA0KICAgIHRoZW1lKGxlZ2VuZC5qdXN0aWZpY2F0aW9uID0gYygxLDEpLCBsZWdlbmQucG9zaXRpb24gPSBjKDAuOTgsIDAuOTgpKSArDQoNCiAgICAjIDMuIFNjYWxpbmcNCiAgICAjIFN0cmV0Y2ggb3V0IHRoZSB4LWF4aXMgc2NhbGUgYSBiaXQgdG8gZml0IG91ciBsYWJlbHMNCiAgICBzY2FsZV94X2RhdGUobGltaXRzID0gYyhhcy5EYXRlKCIyMDIwLTEyLTAxIiksICAgICAgICAgICAgICAgICAgICAgICAgICMgU2V0IGEgc3RhcnQgZGF0ZSBmb3Igb3VyIGxpbWl0DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgYXMuRGF0ZSgiMjAyMy0wMy0wMSIpKSwgIyBJZGVudGlmeSB0aGUgbGFzdCBkYXRlIGFuZCB1c2UgdGhhdA0KICAgICAgICAgICAgICAgICBkYXRlX2JyZWFrcyA9ICIxIG1vbnRoIiwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBIb3cgd2lsbCB3ZSBicmVhayB1cCB0aGUgZGF0ZXM/DQogICAgICAgICAgICAgICAgIGRhdGVfbGFiZWxzID0gIiViLSVZIikgKyANCg0KICAgICMgV2ludGVyIDIwMjAgbG9ja2Rvd24NCiAgICBnZW9tX3RleHQoYWVzKHg9YXMuRGF0ZSgiMjAyMC0xMi0yNiIpICsgNywgbGFiZWwgPSAiUHJvdmluY2Utd2lkZSBsb2NrZG93biIsIHk9MjQwMCksIA0KICAgICAgICAgICAgICBhbmdsZT05MCwgc2l6ZT0xMCwgY29sb3VyPSJibGFjayIpICsNCiAgICBhbm5vdGF0ZSgicmVjdCIsIHhtaW49YXMuRGF0ZSgiMjAyMC0xMi0yNiIpLCB4bWF4PWFzLkRhdGUoIjIwMjAtMTItMjYiKSArIDE0LCANCiAgICAgICAgICAgICB5bWluPS1JbmYsIHltYXg9SW5mLCBmaWxsPSJyZWQiLCBhbHBoYT0wLjIpICsNCg0KICAgICMgU3ByaW5nIDIwMjEgTG9ja2Rvd24NCiAgICBnZW9tX3RleHQoYWVzKHg9YXMuRGF0ZSgiMjAyMS0wNC0wMyIpICsgNywgbGFiZWwgPSAiUHJvdmluY2Utd2lkZSBsb2NrZG93biIsIHk9MjQwMCksIA0KICAgICAgICAgICAgICBhbmdsZT05MCwgc2l6ZT0xMCwgY29sb3VyPSJibGFjayIpICsNCiAgICBhbm5vdGF0ZSgicmVjdCIsIHhtaW49YXMuRGF0ZSgiMjAyMS0wNC0wMyIpLCB4bWF4PWFzLkRhdGUoIjIwMjEtMDQtMDMiKSArIDE0LCANCiAgICAgICAgICAgICB5bWluPS1JbmYsIHltYXg9SW5mLCBmaWxsPSJyZWQiLCBhbHBoYT0wLjIpICsNCg0KICAgICMgT21pY3JvbiBhcnJpdmVzDQogICAgIyMjIDMuMS4wIEFubm90YXRlIHVzaW5nIGEgY3VydmUgDQogICAgZ2VvbV90ZXh0KHg9YXMuRGF0ZSgiMjAyMS0wOS0yNSIpLCBsYWJlbCA9ICJGaXJzdCBPbWljcm9uXG5jYXNlcyByZXBvcnRlZFxuaW4gT250YXJpbyIsIHk9MTAwMCwgDQogICAgICAgICAgICAgIGhqdXN0PTEsIHZqdXN0ID0gMCwgc2l6ZT0xMCwgY29sb3VyPSJibGFjayIpICsNCiAgICBhbm5vdGF0ZSgiY3VydmUiLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgTWFrZSBhIGN1cnZlDQogICAgICAgICAgICAgeD1hcy5EYXRlKCIyMDIxLTEwLTAxIiksIHhlbmQgPSBhcy5EYXRlKCIyMDIxLTExLTI4IiksICAjIFNldCB0aGUgeC1jb29yZGluYXRlcw0KICAgICAgICAgICAgIHk9Li4uLCB5ZW5kPS4uLiwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIFNldCB0aGUgeS1jb29yZGluYXRlcw0KICAgICAgICAgICAgIGxpbmVlbmQgPSAicm91bmQiLCBjdXJ2YXR1cmUgPSAuLi4sICAgICAgICAgICAgICAgICAgICAjIFNldCB0aGUgbGluZSBjaGFyYWN0ZXJpc3RpY3MNCiAgICAgICAgICAgICBjb2xvdXI9InJlZCIsIGxpbmV3aWR0aCA9IDEsIGFycm93ID0gLi4uKSArICAgICAgICAgICAgICAjIEFkZCBhbiBhcnJvdyBhdCB0aGUgZW5kDQoNCiAgICAjIE9udGFyaW8gZW5kcyBwcm9wZXIgUENSIHRlc3RpbmcNCiAgICBnZW9tX3RleHQoYWVzKHg9YXMuRGF0ZSgiMjAyMi0wMi0xMCIpLCBsYWJlbCA9ICJPbnRhcmlvIHJlZHVjZXMgcHVibGljXG5QQ1IgQ09WSUQtMTkgdGVzdGluZyIsIHk9MjUwMCksIA0KICAgICAgICAgICAgICBoanVzdD0wLCBzaXplPTEwLCBjb2xvdXI9ImJsYWNrIikgKw0KICAgIGFubm90YXRlKCJzZWdtZW50IiwgeD1hcy5EYXRlKCIyMDIyLTAyLTAxIiksIHhlbmQgPSBhcy5EYXRlKCIyMDIxLTEyLTMxIiksDQogICAgICAgICAgICAgeT0yNTAwLCB5ZW5kPTI1MDAsIGNvbG91cj0icmVkIiwgbGluZXdpZHRoID0gMSwgYXJyb3cgPSBhcnJvdygpKSANCg0KIyBkaXNwbGF5IG91ciBwbG90DQpwaHVfd2luZG93X2Fubm90YXRlLnBsb3QNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgNC4yLjAgRGF0YSBsYWJlbGluZyB3aXRoIGFubm90YXRpb25zDQoNClVubGlrZSB0aGUgYW5ub3RhdGlvbnMgd2UganVzdCBkaXNjdXNzZWQsIHlvdSBtYXkgd2lzaCB0byBkaXJlY3RseSBsYWJlbCBvciBvdXRwdXQgaW5mb3JtYXRpb24gYmFzZWQgb24geW91ciBkYXRhIGZyb20gdGhlIHBsb3QuIFRoaXMgY2FuIGJlIGluIHRoZSBmb3JtIG9mIGVycm9yIGJhcnMsIG9yIGRhdGEgbGFiZWxzLiBTb21ldGltZXMgeW91IG1heSB3YW50IHRvIGluY2x1ZGUgeW91ciBzYW1wbGUgc2l6ZSBvciBmdXJ0aGVyIGhpZ2hsaWdodCB5b3VyIG91dGxpZXJzLg0KDQojIyMgNC4yLjEgTGFiZWwgZGF0YSBkaXJlY3RseSB3aXRoIHRoZSBgZGlyZWN0bGFiZWxzYCBwYWNrYWdlDQoNCklmIGZvciBzb21lIHJlYXNvbiB5b3UgbmVlZGVkIHRvIGxhYmVsIHlvdXIgcGxvdCBkYXRhIGRpcmVjdGx5LCB0aGUgYGdlb21fZGwoKWAgbGF5ZXIgZnJvbSB0aGUgYGRpcmVjdGxhYmVsc2AgcGFja2FnZXMgY2FuIGJlIHF1aXRlIHVzZWZ1bC4gVGhlIHBhY2thZ2Ugd2lsbCByZXBsYWNlIHlvdXIgY29sb3VyIGxlZ2VuZHMgd2l0aCBkaXJlY3QgbGFiZWxpbmcgaW5zdGVhZC4gVGhpcyBjYW4gKHNvbWV0aW1lcykgYmUgYSBsaXR0bGUgY2xlYW5lciBhbmQgbGVzcyBjb25mdXNpbmcuIFBhcmFtZXRlcnMgeW91IHNob3VsZCBzZXQgd2hlbiB3b3JraW5nIHdpdGggYGdlb21fZGwoKWAgYXJlOg0KDQotICAgYG1ldGhvZGA6IHRoaXMgaXMgdGhlIHBvc2l0aW9uaW5nIG1ldGhvZCBmb3IgdGhlIGRpcmVjdCBsYWJlbCBwbGFjZW1lbnQgYW5kICoqTVVTVCoqIGJlIHNwZWNpZmllZC4gSXQgcGFzc2VzIHBhcmFtZXRlcnMgZnJvbSBhIGBsaXN0YCBvbiB0byB0aGUgYGFwcGx5Lm1ldGhvZCgpYCBmdW5jdGlvbg0KDQogICAgLSAgIG9wdGlvbnMgaW5jbHVkZSBzbWFydC5ncmlkLCBwZXJwZW5kaWN1bGFyLmdyaWQsIGVtcHR5LmdyaWQsIGNsb3Nlc3Qub24uY2h1bGwsIGV4dHJlbWUuZ3JpZCwgZXRjLg0KDQogICAgLSAgIGZpbmQgbW9yZSBvcHRpb25zIFtoZXJlXShodHRwczovL3RkaG9jay5naXRodWIuaW8vZGlyZWN0bGFiZWxzL2RvY3MvaW5kZXguaHRtbCkNCg0KICAgIC0gICBVc2UgYSBgbGlzdCgpYCB0byB1cGRhdGUgYWRkaXRpb25hbCBhdHRyaWJ1dGVzIGxpa2UgZm9udHNpemUgKGNleCksIGZvbnRmYW1pbHksIHJvdGF0aW9uIChyb3QpIGV0Yy4NCg0KLSAgIGBhZXMoKWA6IGxpa2UgYW55IGdlb20sIHlvdSBjYW4gc3BlY2lmeSBhZXN0aGV0aWNzIGluZm9ybWF0aW9uIGluY2x1ZGluZyB0aGUgYGxhYmVsc2AgYW5kIGBjb2xvdXJgLg0KDQpOb3RlIHRoYXQgYWRkaW5nIGRpcmVjdCBsYWJlbHMgdGhpcyB3YXksIGhvd2V2ZXIsIHdpbGwgbm90IHJlbW92ZSB0aGUgY29ycmVzcG9uZGluZyBsZWdlbmQgZnJvbSB0aGUgcGxvdC4gSXQgd2lsbCBzaW1wbHkgYWRkIGV4dHJhIGdlb21zIHRvIHlvdXIgcGxvdC4gV2UnbGwgc2V0IG91ciBsaW5lcyB0byBiZSBsYWJlbGVkIGJ5IHRoZSBwb3NpdGlvbiBvZiB0aGVpciBgbGFzdC5wb2ludHNgIGFuZCBpZiB0aGV5IGFyZSBjbG9zZWx5IHNwYWNlZCB3ZSB3aWxsIGBidW1wdXBgIHRoZSB2YXJpb3VzIGVudHJpZXMuDQoNCkFsdGVybmF0aXZlbHkgeW91IGNhbiB1c2UgdGhlIGBsYXN0LmJ1bXB1cGAgbWV0aG9kIGJ1dCBpdCBhcHBlYXJzIHRvIGJlIGJyb2tlbiBpbiB0aGUgY3VycmVudCB2ZXJzaW9uIG9mIGBkaXJlY3RsYWJlbHNgIC4NCg0KYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD0xMn0NCnBodV93aW5kb3dfYW5ub3RhdGUucGxvdCArDQogICAgIyBVcGRhdGUgdGhlIGxhYmVsaW5nIG9mIG91ciBsaW5lcw0KICAgIGdlb21fZGwobWV0aG9kPWxpc3QoLi4uLA0KICAgICAgICAgICAgICAgICAgICAgICAgIyBEZWZpbmUgImhvdyIgd2Ugd2FudCB0ZXh0IG9yZGVyZWQNCiAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZD0uLi4pLCANCiAgICAgICAgICAgIGFlcyhsYWJlbD0uLi4pKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgNC4yLjIgTGFiZWwgZGF0YSB1c2luZyB0aGUgYGRpcmVjdC5sYWJlbCgpYCBmZWF0dXJlDQoNCkZvciBzaW1wbGljaXR5LCB5b3UgY2FuIGFsc28gY2FsbCBvbiBgZGlyZWN0LmxhYmVsKClgIGZyb20gdGhlIGBkaXJlY3RsYWJlbHNgIHBhY2thZ2UsIHdoaWNoIHdpbGwgYXV0b21hdGljYWxseSByZW1vdmUgdGhlIGFzc29jaWF0ZWQgbGVnZW5kIGZyb20geW91ciBwbG90LiBZb3UgY2FuIHVzZSBpdCBieSBwcm92aWRpbmcgdGhlIGZvbGxvd2luZyBwYXJhbWV0ZXJzOg0KDQotICAgYHBgOiB0aGUgZ2dwbG90IG9iamVjdCB5b3UndmUgYWxyZWFkeSBjcmVhdGVkLg0KDQotICAgYG1ldGhvZGAgdGhlIHBvc2l0aW9uaW5nIG1ldGhvZCBhcyB3aXRoIGBnZW9tX2RsKClgLg0KDQogICAgLSAgIEZvciB0aGUgbWV0aG9kIGNob2ljZSB5b3UgY2FuIHNldCBpdCB0byBgZGwuY29tYmluZSgpYCBhbmQgaW5jbHVkZSBzZXZlcmFsIHBvc2l0aW9uaW5nIG1ldGhvZHMgYXQgdGhlIHNhbWUgdGltZS4NCg0KICAgIC0gICBVc2UgYSBgbGlzdCgpYCB0byB1cGRhdGUgYWRkaXRpb25hbCBhdHRyaWJ1dGVzIGxpa2UgZm9udHNpemUgKGNleCksIGZvbnRmYW1pbHksIHJvdGF0aW9uIChyb3QpIGV0Yy4gVG8gZG8gdGhpcywgeW91IG11c3QgKmFsc28qIGluY2x1ZGUgeW91ciBtZXRob2QgaW4gdGhlIGxpc3QsICphZnRlciogeW91ciBhdHRyaWJ1dGUgY2hhbmdlcy4NCg0KYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD0xMn0NCiMgVXNlIGRpcmVjdC5sYWJlbCgpIHRvIHJlZm9ybWF0IHlvdXIgcGxvdA0KLi4uKHAgPSBwaHVfd2luZG93X2Fubm90YXRlLnBsb3QsICAgICAjIFByb3ZpZGUgYSBwbG90IG9iamVjdA0KICAgICAgICAgICAgIG1ldGhvZD1saXN0KGNleD0yLCBsaXN0KCJsYXN0LnBvaW50cyIsICJidW1wdXAiKSkpICAgIyBEZXRhaWwgdGhlIGZvcm1hdCBpbmZvcm1hdGlvbiBmb3IgeW91ciBsYWJlbGluZw0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyA0LjMuMCBFbXBoYXNpemUgeW91ciBkYXRhIGdyb3VwcyB3aXRoIGBnZ2hpZ2hsaWdodCgpYA0KDQpZb3UgbWF5IGZpbmQgeW91cnNlbGYgaW4gYW4gaW5zdGFuY2Ugd2hlcmUgeW91IGhhdmUgdG9vIG1hbnkgZGF0YSBncm91cHMgdG8gcHJlc2VudCAoaWUgMzQgUEhVcykgYnV0IHdvdWxkIHN0aWxsIGxpa2UgdGhlIGF1ZGllbmNlIHRvIGdldCBhbiBvdmVydmlldyBvZiB5b3VyIGRhdGFzZXQgd2hpbGUgZm9jdXNpbmcgb24gYSBmZXcgaXRlbXMuIEFzIHdlIGhhdmUgZG9uZSBpbiB0aGUgcGFzdCwgeW91IGNvdWxkIGJyZWFrIGdyb3VwcyBvdXQgdXNpbmcgYGZhY2V0XyooKWAgYnV0IHRoYXQgaXNuJ3QgYWx3YXlzIGlkZWFsLiBXZSBoYXZlIGFsc28gZmlsdGVyZWQgZm9yIHRoZSB0b3AgUEhVcyBmcm9tIGEgcHJldmlvdXNseSBnZW5lcmF0ZWQgbGlzdCBidXQgdGhlbiB3ZSBnZXQgbm8gc2Vuc2Ugb2YgdGhlIG90aGVyIFBIVXMgYXQgYWxsLg0KDQpJbnN0ZWFkIHlvdSBjYW4gdXNlIHRoZSBgZ2doaWdobGlnaHQoKWAgbGF5ZXIgZnJvbSB0aGUgcGFja2FnZSBvZiB0aGUgc2FtZSBuYW1lLiBTb21lIGhlbHBmdWwgcGFyYW1ldGVycyBmcm9tIHRoaXMgbGF5ZXIgaW5jbHVkZToNCg0KLSAgIGAuLi5gOiB0aGUgZXhwcmVzc2lvbnMgeW91IHdpbGwgdXNlIHRvIGZpbHRlciBkYXRhIChpZSB5b3VyIGBwcmVkaWNhdGVgKSB3aGljaCB3aWxsIGJlIHBhc3NlZCB0byBgZHBseXI6OmZpbHRlcigpYC4NCg0KLSAgIGBtYXhfaGlnaGxpZ2h0YDogdGhlIG1heGltdW0gbnVtYmVyIG9mIHNlcmllcyB0byBoaWdobGlnaHQuDQoNCi0gICBgdW5oaWdobGlnaHRlZF9wYXJhbXNgOiB0aGUgYWVzdGhldGljcyBmb3IgeW91ciB1bmhpZ2hsaWdodGVkIGdyb3Vwcy4NCg0KLSAgIGB1c2VfZ3JvdXBfYnlgOiBpZiBUUlVFLCB0aGlzIGZ1bmN0aW9uIHdpbGwgdXNlIGBkcGx5cjo6Z3JvdXBfYnkoKWAgdG8gZXZhbHVhdGUgeW91ciBgcHJlZGljYXRlYC4NCg0KLSAgIGB1c2VfZGlyZWN0X2xhYmVsYDogaWYgVFJVRSwgbGFiZWxzIHdpbGwgYmUgYWRkZWQgZGlyZWN0bHkgdG8gdGhlIHBsb3QgaW5zdGVhZCBvZiB1c2luZyBhIGxlZ2VuZC4NCg0KLSAgIGBsYWJlbF9rZXlgOiB0aGUgY29sdW1uIG5hbWUgZm9yIGxhYmVsIGFlc3RoZXRpY3MuDQoNCi0gICBgbGFiZWxfcGFyYW1zYDogYSBsaXN0IG9mIGFlc3RoZXRpY3MgY3VzdG9taXphdGlvbnMgbGlrZSBgc2l6ZWAuDQoNCkxldCdzIHBsb3QgYWxsIG9mIG91ciBQSFUgZGF0YSBvbnRvIHRoZSBncmFwaCBhbmQgb25seSBoaWdobGlnaHQgdGhlIHRvcCA0IFBIVXMgYXMgYmVmb3JlLiBXZSdsbCBoYXZlIHRvIGRvIHNvbWUgZXh0cmEgZmlkZGxpbmcgdG8gbWFrZSBpdCB3b3JrIGp1c3QgcmlnaHQuDQoNCmBgYHtyLCBmaWcud2lkdGg9MjAsIGZpZy5oZWlnaHQ9MTB9DQojIFRoaXMgaXMgZ29pbmcgdG8gYmUgYSBzaW1wbGVyIGdyYXBoIHNvIGFkanVzdCBvdXIgcGxvdCB3aW5kb3cgc2l6ZSBhY2NvcmRpbmdseQ0Kb3B0aW9ucyhyZXByLnBsb3Qud2lkdGg9MjAsIHJlcHIucGxvdC5oZWlnaHQ9MTApDQoNCmdnaCA8LQ0KICAjIEJ1aWxkIG91ciBwbG90IGFuZCBzYXZlIHRvIGFuIG9iamVjdA0KICBjb3ZpZF9waHVfd2luZG93LmRmICU+JSANCiAgIyBGaWx0ZXIgZm9yIHRoZSB0b3AgNSBpbmZlY3RlZCBQSFVzDQogIG11dGF0ZShwdWJsaWNfaGVhbHRoX3VuaXQgPSBmY3RfcmVvcmRlcihwdWJsaWNfaGVhbHRoX3VuaXQsIHdpbmRvd19tZWFuLCAuZGVzYz1UUlVFKSkNCiAgDQogIHBodV9jYXNlcy5wbG90IDwtDQogICMgcmVkaXJlY3QgdGhlIGZpbHRlcmVkIHJlc3VsdCB0byBnZ3Bsb3QNCiAgIyAxLiBEYXRhDQogIGdncGxvdChnZ2gpICsNCiAgICAjIDIuIEFlc3RoZXRpY3MNCiAgICBhZXMoeCA9IHN0YXJ0X2RhdGUsIHkgPSB3aW5kb3dfbWVhbiwgY29sb3VyID0gcHVibGljX2hlYWx0aF91bml0KSArDQoNCiAgICAjIFN0YXJ0IHdpdGggYSBiYXNlIHRoZW1lDQogICAgdGhlbWVfbWluaW1hbCgpICsNCg0KICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0yMCksICMgc2V0IHRleHQgc2l6ZSB0byAyMA0KICAgICAgICAgIA0KICAgICAgICAgICMgTW92ZSB0aGUgbGVnZW5kIGFyb3VuZCB0byB3aXRoaW4gdGhlIHBhbmVsIHNwYWNlDQogICAgICAgICAgbGVnZW5kLmp1c3RpZmljYXRpb24gPSBjKDEsMSksDQogICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gYygwLjk4LDAuOTgpLA0KICAgICAgICAgIA0KICAgICAgICAgICMgVXBkYXRlIHRoZSBwYW5lbCB0byBkcm9wIHRoZSBtaW5vciBheGlzIGdyaWQgbGluZXMNCiAgICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgIA0KICAgICAgICAgICMgVXNlIGEgYmxhY2sgbGluZSBmb3IgdGhlIGF4ZXMNCiAgICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiksDQogICAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KGNvbG91ciA9ICJibGFjayIsIGZhY2U9ImJvbGQiKSwNCiAgICAgICAgICANCiAgICAgICAgICAjIEFkanVzdCB0aGUgeC1heGlzIHRleHQNCiAgICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCAgIyBSb3RhdGUgOTANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoanVzdCA9IDEsICAgIyBSaWdodC1qdXN0aWZ5DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmp1c3QgPSAwLjUpICMgQ2VudHJlIHRleHQgInZlcnRpY2FsbHkiIG9uIGF4aXMgdGljaw0KICAgICAgICAgKSArDQoNCiAgICAjIEFkZCBsYWJlbHMgdG8gb3VyIHBsb3QNCiAgICBsYWJzKHRpdGxlID0gIk1lYW4gY2FzZXMgb2YgQ09WSUQtMTkgaW4gYSAxNC1kYXkgd2luZG93IGFjcm9zcyB0b3AgNCBPbnRhcmlvIFB1YmxpYyBIZWFsdGggVW5pdHNcbiIsDQogICAgICAgICB4ID0gIlxuV2luZG93IGRhdGUiLA0KICAgICAgICAgeSA9ICJNZWFuIGNhc2VzIGluIDE0LWRheSB3aW5kb3dcbiIsDQogICAgICAgICBjb2xvdXIgPSAiUHVibGljIEhlYWx0aCBVbml0IiwNCiAgICAgICAgIGNhcHRpb24gPSAiKjE0LWRheSByb2xsaW5nIG1lYW4gd2l0aCBkYXRlIGFzIHN0YXJ0IG9mIHRoZSB3aW5kb3ciKSArDQoNCiAgICAjIDMuIFNjYWxpbmcNCiAgICAjIFN0YXJ0IGxvb2tpbmcgYXQgZGF0YSBmcm9tIEp1bHkgMjAyMCBvbndhcmRzDQogICAgc2NhbGVfeF9kYXRlKGxpbWl0cyA9IGMoYXMuRGF0ZSgiMjAyMC0xMi0wMSIpLCAgICAgICAgICAgICAgICAgICAgICAgICAjIFNldCBhIHN0YXJ0IGRhdGUgZm9yIG91ciBsaW1pdA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzLkRhdGUoIjIwMjMtMDMtMDEiKSksICAgICAgICAgICAgICAgICAgICAgICAgIyBTZXQgdGhlIGVuZCBkYXRlIGluIHlvdXIgbGltaXQNCiAgICAgICAgICAgICAgICAgZGF0ZV9icmVha3MgPSAiMSBtb250aCIsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgSG93IHdpbGwgd2UgYnJlYWsgdXAgdGhlIGRhdGVzPw0KICAgICAgICAgICAgICAgICBkYXRlX2xhYmVscyA9ICIlYi0lWSIpICsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBIb3cgd2lsbCB3ZSBmb3JtYXQgbGFiZWxzDQoNCiAgICAjIENoYW5nZSBvdXIgeS1heGlzIGJyZWFrcw0KICAgIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKC0xMCwgMzUwMCksIGJyZWFrcyA9IHNlcSgwLCAzNTAwLCA1MDApKSArDQoNCiAgICAjIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0gU2VjdGlvbiA0LjMuMCBoaWdobGlnaHRpbmcgc3BlY2lmaWMgZ2VvbXMgLS0tLS0tLS0tLS0tLS0tLS0tLS0gIyMjDQoNCiAgICAjIDQuIEdlb21zDQogICAgIyMjIFBsb3QgYWxsIG9mIG91ciBwdWJsaWMgaGVhbHRoIHVuaXQgZGF0YQ0KICAgIC4uLihsaW5ld2lkdGg9MSwgDQogICAgICAgICAgICAgIGFlcyh4PXN0YXJ0X2RhdGUsIA0KICAgICAgICAgICAgICAgICAgeT13aW5kb3dfbWVhbiwgDQogICAgICAgICAgICAgICAgICBncm91cCA9IHB1YmxpY19oZWFsdGhfdW5pdCwgDQogICAgICAgICAgICAgICAgICBjb2xvdXIgPSBwdWJsaWNfaGVhbHRoX3VuaXQpKSArDQoNCiAgICAjIyMgSGlnaGxpZ2h0IGp1c3QgdGhlIHRvcCA0IFBIVXMNCiAgICAuLi4ocHVibGljX2hlYWx0aF91bml0ICVpbiUgcGh1X2J5X3RvdGFsX2Nhc2VzX2Rlc2NbMTo0XSwgICMgRmlsdGVyIHlvdXIgZGF0YQ0KICAgICAgICAgICAgICAgIHVzZV9ncm91cF9ieSA9IEZBTFNFLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIERvbid0IGdyb3VwIGl0DQogICAgICAgICAgICAgICAgbGFiZWxfcGFyYW1zID0gbGlzdChzaXplID0gMTApKSArICAgICAgICAgICAgICAgICAgICAgICMgU2V0IHRoZSBsYWJlbHMgdG8gc2l6ZSAxMCANCg0KICAgICMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLSBTZWN0aW9uIDQuMy4wIGhpZ2hsaWdodGluZyBzcGVjaWZpYyBnZW9tcyAtLS0tLS0tLS0tLS0tLS0tLS0tLSAjIyMNCg0KICAgICMgOC4gQW5ub3RhdGlvbnMNCiAgICAjIFdpbnRlciAyMDIwIGxvY2tkb3duDQogICAgZ2VvbV90ZXh0KGFlcyh4PWFzLkRhdGUoIjIwMjAtMTItMjYiKSArIDcsIGxhYmVsID0gIlByb3ZpbmNlLXdpZGUgbG9ja2Rvd24iLCB5PTI0MDApLCANCiAgICAgICAgICAgICAgYW5nbGU9OTAsIHNpemU9MTAsIGNvbG91cj0iYmxhY2siKSArDQogICAgYW5ub3RhdGUoInJlY3QiLCB4bWluPWFzLkRhdGUoIjIwMjAtMTItMjYiKSwgeG1heD1hcy5EYXRlKCIyMDIwLTEyLTI2IikgKyAxNCwgDQogICAgICAgICAgICAgeW1pbj0tSW5mLCB5bWF4PUluZiwgZmlsbD0icmVkIiwgYWxwaGE9MC4yKSArDQoNCiAgICAjIFNwcmluZyAyMDIxIExvY2tkb3duDQogICAgZ2VvbV90ZXh0KGFlcyh4PWFzLkRhdGUoIjIwMjEtMDQtMDMiKSArIDcsIGxhYmVsID0gIlByb3ZpbmNlLXdpZGUgbG9ja2Rvd24iLCB5PTI0MDApLCANCiAgICAgICAgICAgICAgYW5nbGU9OTAsIHNpemU9MTAsIGNvbG91cj0iYmxhY2siKSArDQogICAgYW5ub3RhdGUoInJlY3QiLCB4bWluPWFzLkRhdGUoIjIwMjEtMDQtMDMiKSwgeG1heD1hcy5EYXRlKCIyMDIxLTA0LTAzIikgKyAxNCwgDQogICAgICAgICAgICAgeW1pbj0tSW5mLCB5bWF4PUluZiwgZmlsbD0icmVkIiwgYWxwaGE9MC4yKSArDQoNCiAgICAjIE9taWNyb24gYXJyaXZlcw0KICAgICMgQW5ub3RhdGUgdXNpbmcgYSBjdXJ2ZSANCiAgICBnZW9tX3RleHQoeD1hcy5EYXRlKCIyMDIxLTA5LTI1IiksIGxhYmVsID0gIkZpcnN0IE9taWNyb25cbmNhc2VzIHJlcG9ydGVkXG5pbiBPbnRhcmlvIiwgeT0xMDAwLCANCiAgICAgICAgICAgICAgaGp1c3Q9MSwgdmp1c3QgPSAwLCBzaXplPTEwLCBjb2xvdXI9ImJsYWNrIikgKw0KICAgIGFubm90YXRlKCJjdXJ2ZSIsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBNYWtlIGEgY3VydmUNCiAgICAgICAgICAgICB4PWFzLkRhdGUoIjIwMjEtMTAtMDEiKSwgeGVuZCA9IGFzLkRhdGUoIjIwMjEtMTEtMjgiKSwgICMgU2V0IHRoZSB4LWNvb3JkaW5hdGVzDQogICAgICAgICAgICAgeT0xMDAwLCB5ZW5kPTEwMCwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIFNldCB0aGUgeS1jb29yZGluYXRlcw0KICAgICAgICAgICAgIGxpbmVlbmQgPSAicm91bmQiLCBjdXJ2YXR1cmUgPSAtMC41LCAgICAgICAgICAgICAgICAgICAgIyBTZXQgdGhlIGxpbmUgY2hhcmFjdGVyaXN0aWNzDQogICAgICAgICAgICAgY29sb3VyPSJyZWQiLCBsaW5ld2lkdGggPSAxLCBhcnJvdyA9IGFycm93KCkpICsgICAgICAgICAgICAgICMgQWRkIGFuIGFycm93IGF0IHRoZSBlbmQNCg0KICAgICMgT250YXJpbyBlbmRzIHByb3BlciBQQ1IgdGVzdGluZw0KICAgIGdlb21fdGV4dChhZXMoeD1hcy5EYXRlKCIyMDIyLTAyLTEwIiksIGxhYmVsID0gIk9udGFyaW8gcmVkdWNlcyBwdWJsaWNcblBDUiBDT1ZJRC0xOSB0ZXN0aW5nIiwgeT0yNTAwKSwgDQogICAgICAgICAgICAgIGhqdXN0PTAsIHNpemU9MTAsIGNvbG91cj0iYmxhY2siKSArDQogICAgYW5ub3RhdGUoInNlZ21lbnQiLCB4PWFzLkRhdGUoIjIwMjItMDItMDEiKSwgeGVuZCA9IGFzLkRhdGUoIjIwMjEtMTItMzEiKSwNCiAgICAgICAgICAgICB5PTI1MDAsIHllbmQ9MjUwMCwgY29sb3VyPSJyZWQiLCBsaW5ld2lkdGggPSAxLCBhcnJvdyA9IGFycm93KCkpIA0KDQojIHBsb3Qgb3VyIGRhdGENCnBodV9jYXNlcy5wbG90DQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMgNS4wLjAgQW5ub3RhdGlvbnMgYW5kIHRoZW1lIGFsdGVyYXRpb25zIHRocm91Z2ggb3RoZXIgbGF5ZXJzDQoNCiMjIDUuMS4wIEFubm90YXRlIGVycm9yIGJhcnMgd2l0aCBgZ2VvbV8qKClgDQoNCldoZW4gd29ya2luZyB3aXRoIGJhciBvciBsaW5lIHBsb3RzIHdoZXJlIHlvdSBtYXkgaGF2ZSBnZW5lcmF0ZWQgaW5mb3JtYXRpb24gc3VjaCBhcyBhIG1lYW4gd2l0aCBzdGFuZGFyZCBkZXZpYXRpb24sIHlvdSBjYW4gcGxvdCB0aGF0IGluZm9ybWF0aW9uIHdpdGggYGdlb21fZXJyb3JiYXIoKWAuIFVubGlrZSBhbm5vdGF0aW9ucyBmcm9tIGFib3ZlIHRoaXMgaXMgYSBzcGVjaWZpYyBnZW9tIGFuZCBpcyB0cmVhdGVkIGJ5IHRoZSBwbG90IGxpa2UgYW55IG90aGVyIGBnZW9tXyooKWAgd2UndmUgZW5jb3VudGVyZWQuIFVuZGVyIGl0J3MgYGFlcygpYCBhcmd1bWVudCB5b3UgY2FuIHNwZWNpZnkgdGhlIGB5bWluYCBhbmQgYHltYXhgIHZhbHVlcyBvciBkYXRhIHNvdXJjZXMuIElmIHlvdSBhbHJlYWR5IGhhdmUgZ2VuZXJhdGVkIHZhcmlhYmxlcyAoY29sdW1ucykgZm9yIHRoZXNlIHZhbHVlcywgeW91IGNhbiB1c2UgdGhlbSBkaXJlY3RseSBvciB5b3UgY2FuIGNhbGN1bGF0ZSB0aGVtIG9uIHRoZSBmbHkgaWYgeW91IGhhdmUganVzdCBhIG1lYW4gYW5kIHN0YW5kYXJkIGRldmlhdGlvbi4NCg0KVGhlcmUgYXJlIGFsdGVybmF0aXZlIGZvcm1hdHMgb2YgdGhlIGBnZW9tX2Vycm9yYmFyKClgIGFzIHdlbGw6DQoNCnwgZ2VvbSAgICAgICAgICAgICAgfCBEZXNjcmlwdGlvbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnw6LS0tLS0tLS0tLS0tLS0tLS0tLS0tLXw6LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfA0KfCBnZW9tX2Nyb3NzYmFyKCkgICB8IEEgaG9sbG93IGJveCB3aXRoIHRoZSBtaWRkbGUgaW5kaWNhdGVkIGJ5IGEgaG9yaXpvbmFsIGxpbmUuICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBnZW9tX2Vycm9yYmFyaCgpICB8IEhvcml6b250YWwgdmVyc2lvbnMgb2YgdGhlIGVycm9yYmFyLiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBnZW9tX2xpbmVyYW5nZSgpICB8IERyYXdzIGFuIGludGVydmFsIHVzaW5nIGEgc2luZ2xlIHZlcnRpY2FsIGxpbmUuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBnZW9tX3BvaW50cmFuZ2UoKSB8IFNhbWUgYXMgYSBsaW5lcmFuZ2UgZXhjZXB0IGFuIGFkZGl0aW9uYWwgcG9pbnQgaXMgcGxvdHRlZCBpbiB0aGUgbWlkZGxlIG9mIHRoZSByYW5nZS4gfA0KDQpMZXQncyByZWNyZWF0ZSBvbmUgb2Ygb3VyIHBsb3RzIGZyb20gbGVjdHVyZSAyIHVzaW5nIHN1bW1hcnkgZGF0YSBhbmQgc29tZSBvZiB0aGVzZSBuZXcgZ2VvbXMhDQoNCmBgYHtyLCBmaWcud2lkdGg9MjAsIGZpZy5oZWlnaHQ9MTB9DQoNCmNvdmlkX2RlbW9ncmFwaGljcy5wbG90IDwtDQogIGNvdmlkX2RlbW9ncmFwaGljc190b3RhbC5kZiAlPiUgDQogICMgVW5ncm91cCB0aGlzIGRhdGFmcmFtZSB0byBjbGVhbiBpdCB1cCBhIGxpdHRsZQ0KICB1bmdyb3VwKCkgJT4lIA0KICAjIEZpbHRlciBmb3IgY3VtdWxhdGl2ZSBkYXRhIG9ubHkNCiAgZmlsdGVyKHBlcmlvZCA9PSAiY3VtdWxhdGl2ZSIpICU+JSANCiAgIyBTZWxlY3QgZm9yIGp1c3QgdGhlIGltcG9ydGFudCBjb2x1bW5zDQogIHNlbGVjdChwdWJsaWNfaGVhbHRoX3VuaXQsIGFnZV9ncm91cCwgcGVyY2VudF9jYXNlcywgcGVyY2VudF9ob3NwaXRhbGl6YXRpb25zKSAlPiUgDQogICMgUGl2b3QgdGhlIG1vZGlmaWVkIHRhYmxlIHRvIGNhcHR1cmUgdGhlICJzdGF0X2dyb3VwIiBvZiBwZXJjZW50X2Nhc2VzIHZzIHBlcmNlbnRfZGVhdGhzDQogIHBpdm90X2xvbmdlcihjb2xzPWMoMyw0KSwgbmFtZXNfdG8gPSAic3RhdF9ncm91cCIsIHZhbHVlc190byA9ICJwZXJjZW50X1BIVV90b3RhbCIpICU+JSANCiAgIyBHcm91cCB0aGUgZGF0YSBib3RoIGJ5IGFnZSBncm91cCBhbmQgdGhlbiBzdGF0IGdyb3VwDQogIGdyb3VwX2J5KGFnZV9ncm91cCwgc3RhdF9ncm91cCkgJT4lIA0KICAjIEdlbmVyYXRlIHNvbWUgc3VtbWFyeSBzdGF0aXN0aWNzDQogIHN1bW1hcmlzZShtZWFuID0gbWVhbihwZXJjZW50X1BIVV90b3RhbCksIA0KICAgICAgICAgICAgc2QgPSBzZChwZXJjZW50X1BIVV90b3RhbCksDQogICAgICAgICAgICBtZWRpYW4gPSBtZWRpYW4ocGVyY2VudF9QSFVfdG90YWwpKSAlPiUgICANCiAgDQogICMgUGxvdCB0aGUgZGF0YSBhcyBhIG1peHR1cmUgb2YgbXVsdGlwbGUgZ2VvbXMNCiAgDQogICMgMS4gRGF0YQ0KICBnZ3Bsb3QoLikgKw0KICAgICMgMi4gQWVzdGhldGljcw0KICAgIGFlcyh4PWFnZV9ncm91cCwgeSA9IG1lYW4sIGxpbmV0eXBlPXN0YXRfZ3JvdXApICsNCg0KICAgICMgVGhlbWVzDQogICAgdGhlbWVfbWluaW1hbCgpICsNCg0KICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0yMCksICMgc2V0IHRleHQgc2l6ZSB0byAyMA0KIA0KICAgICAgICAgICMgVXBkYXRlIHRoZSBwYW5lbCB0byBkcm9wIHRoZSBtaW5vciBheGlzIGdyaWQgbGluZXMNCiAgICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgIA0KICAgICAgICAgICMgVXNlIGEgYmxhY2sgbGluZSBmb3IgdGhlIGF4ZXMNCiAgICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiksDQogICAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KGNvbG91ciA9ICJibGFjayIsIGZhY2U9ImJvbGQiKSwNCiAgICAgICAgICkgKw0KDQogICAgIyBBZGQgbGFiZWxzIHRvIG91ciBwbG90DQogICAgIyBBZGQgbGFiZWxzIHRvIHRoZSBwbG90DQogICAgbGFicyh0aXRsZSA9ICJQZXJjZW50IGNhc2VzIGFuZCBkZWF0aHMgYnkgcHJvcG9ydGlvbiBwZXIgUEhVIGFjcm9zcyBhZ2UgZ3JvdXAiLA0KICAgICAgICAgeCA9ICJcbkFnZSBncm91cCIsDQogICAgICAgICB5ID0gIlByb3BvcnRpb24gb2YgcmVwb3J0ZWQgUEhVIGRhdGFcbiIsDQogICAgICAgICBjYXB0aW9uID0gIlxuKkFnZSBncm91cCB2YWx1ZXMgYXJlIGNhbGN1bGF0ZWQgYXMgYSBwZXJjZW50YWdlIG9mIHRvdGFsIGNhc2VzIG9yIGRlYXRocyB3aXRoaW4gYSBQSFUiKSArDQoNCiAgICAjIDQuIERhdGENCiAgICAjIyMgNS4xLjAgQWRkIGFuIGVycm9yYmFyIHRvIHJlcHJlc2VudCB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uIHJhbmdlDQogICAgLi4uKHdpZHRoID0gMC4yLCBhZXMoeSA9IG1lYW4sIHltaW4gPSAuLi4sIHltYXggPSAuLi4sIGNvbG91cj1zdGF0X2dyb3VwKSwgc2l6ZT0xKSArDQoNCiAgICAjIyMgNS4xLjAgQWRkIGEgcG9pbnQgdG8gcmVwcmVzZW50IHRoZSBtZWFuIG9mIGVhY2ggZXJyb3IgYmFyDQogICAgZ2VvbV9wb2ludChhZXMoeT1tZWFuLCBzaGFwZT1hZ2VfZ3JvdXAsIGdyb3VwID0gc3RhdF9ncm91cCksIHNpemUgPSA1KQ0KDQpjb3ZpZF9kZW1vZ3JhcGhpY3MucGxvdA0KYGBgDQoNCmBgYHtyLCBmaWcud2lkdGg9MjAsIGZpZy5oZWlnaHQ9MTB9DQpjb3ZpZF9kZW1vZ3JhcGhpY3MucGxvdCArIA0KICAjIEFkZCBhIGxpbmUgdG8gY29ubmVjdCBvdXIgYWdlIGdyb3Vwcw0KICBnZW9tX2xpbmUoYWVzKHg9YWdlX2dyb3VwLCB5PS4uLiwgZ3JvdXAgPSBzdGF0X2dyb3VwLCBjb2xvdXI9c3RhdF9ncm91cCksIGxpbmV3aWR0aD0xKQ0KYGBgDQoNCk5vdyB0aGF0IHdlJ3ZlIGdvbmUgYW5kIGJ1aWx0IG91cnNlbHZlcyBhbiBleHRyZW1lbHkgc3RyYW5nZSBwbG90LCAocmVtZW1iZXIsIHRoaXMgaXMganVzdCBhbiBleGFtcGxlKSB0aGVyZSBhcmUgYSBmZXcgdGhpbmdzIHdlIGNhbiBmaXgvcGxheSB3aXRoLg0KDQoxLiAgWW91J2xsIG5vdGUgdGhhdCB3ZSBvbmx5IGdldCA2IHNoYXBlcyBwbG90dGVkIGZvciBvdXIgYWdlIGdyb3Vwcy4NCjIuICBUaGUgbGVnZW5kIHRpdGxlcyBzaG91bGQgYmUgY29ycmVjdGVkLg0KMy4gIFRoZSBkaWZmZXJlbnQgbGVnZW5kcyBzaG91bGQgYmUgc2h1ZmZsZWQgYXJvdW5kIGluIG9yZGVyIHNvIHRoYXQgYGFnZV9ncm91cGAgaXMgb24gdG9wLg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgNS4yLjAgTW9yZSBsZWdlbmQgYWx0ZXJhdGlvbnMgd2l0aCB0aGUgYGd1aWRlYCBwYXJhbWV0ZXIgb3IgYGd1aWRlcygpYCBsYXllcg0KDQpXZSd2ZSBhbHJlYWR5IGxvb2tlZCBhdCBzb21lIGhlbHBmdWwgbGVnZW5kIGFsdGVyYXRpb25zIHBlcnRhaW5pbmcgdG8gcG9zaXRpb25pbmcgYW5kIHRleHQgcmVsYWJlbGluZyBpbiBzZWN0aW9uICoqMi4wLjAqKi4gTm93IHdlJ2xsIGV4cGxvcmUgc29tZSBvZiB0aGUgcmVtYWluaW5nIHRpcHMgYW5kIHRyaWNrcyB3aGVuIGl0IGNvbWVzIHRvIHdvcmtpbmcgd2l0aCBtdWx0aXBsZSBsZWdlbmRzIHdpdGhpbiB5b3VyIHBsb3QuDQoNClJlY2FsbCB0aGF0IHdpdGhpbiBlYWNoIG9mIHRoZSBndWlkZSB0eXBlcywgeW91IGNhbiB1cGRhdGUgcGFyYW1ldGVycyBhYm91dCB0ZXh0IHdpdGhpbiB0aGUgbGVnZW5kLg0KDQp8IENvbXBvbmVudCB8IFN1Yi1jb21wb25lbnRzICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnw6LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18Oi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18DQp8IHRpdGxlICAgICB8IG5hbWUsIHBvc2l0aW9uLCB0aGVtZSwgaGp1c3QsIHZqdXN0ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgbGFiZWwgICAgIHwgbmFtZSwgcG9zaXRpb24sIHRoZW1lLCBoanVzdCwgdmp1c3QgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBrZXkgICAgICAgfCB3aWR0aCwgaGVpZ2h0ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IG9yZGVyICAgICB8IHlvdSBjYW4gZGV0ZXJtaW5lIHRoZSBvcmRlciBvZiB0aGUgZ3VpZGUgYW1vbmdzdCBvdGhlcnMgdXNpbmcgaW50ZWdlcnMgWzE6OTldLiAwIHNldHMgb3JkZXIgYnkgYW4gYWxnb3JpdGhtIHwNCnwgb3RoZXIgICAgIHwgZGlyZWN0aW9uIG9mIGd1aWRlLCBudW1iZXIgb2Ygcm93cy9jb2xzICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KDQpXZSdsbCB0YWtlIGEgY2xvc2VyIGxvb2sgYXQgdGhlIGBvcmRlcmAgcGFyYW1ldGVyIG5leHQgdXNpbmcgb3VyIGFib3ZlIHZpc3VhbGl6YXRpb24gb2YgdGhlIGFnZS1ncm91cGVkIGRhdGEuDQoNCmBgYHtyLCBmaWcud2lkdGg9MjAsIGZpZy5oZWlnaHQ9MTB9DQoNCmNvdmlkX2RlbW9ncmFwaGljcy5wbG90ICsgDQogICMgMi4gQWVzdGhldGljcw0KICAjIyMgNS4yLjAgU2V0IG91ciBndWlkZSBwb3NpdGlvbnMgZm9yIGxpbmV0eXBlIGFuZCBjb2xvdXIgdG8gMg0KICBndWlkZXMobGluZXR5cGUgPSAibm9uZSIsDQogICAgICAgICBjb2xvdXIgPSBndWlkZV9sZWdlbmQodGl0bGU9IkluZGljYXRvciIsIG9yZGVyPS4uLikpICsNCg0KICAjIDMuIFNjYWxpbmcNCiAgIyMjIDUuMi4wIHJlbmFtZSBvdXIgeC1heGlzIGxhYmVscyB1c2luZyBwaXBlcyENCiAgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHM9Y292aWRfZGVtb2dyYXBoaWNzX3RvdGFsLmRmJGFnZV9ncm91cCAlPiUgbGV2ZWxzKCkgJT4lIGFzLmNoYXJhY3RlcigpICU+JSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBVc2Ugc3RyaW5nIHJlcGxhY2VtZW50IHRvIGNoYW5nZSBvdXIgbGFiZWxzDQogICAgICAgICAgICAgICAgICAgc3RyX3JlcGxhY2VfYWxsKHBhdHRlcm49IiB0byAiLCByZXBsYWNlbWVudCA9ICItIikgDQogICAgICAgICAgICAgICAgICApICsNCg0KICAjIyMgNS4yLjAgZ2dwbG90IG9ubHkgYWRkcyA2IHNoYXBlcyBhdXRvbWF0aWNhbGx5IHNvIHdlIG5lZWQgdG8gYWRkIG1vcmUgbWFudWFsbHkNCiAgIyBTZXQgdmFsdWVzIGJhc2VkIG9uIG51bWJlciBvZiBsZXZlbHMNCiAgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcz1jKDE6bmxldmVscyhjb3ZpZF9kZW1vZ3JhcGhpY3NfdG90YWwuZGYkYWdlX2dyb3VwKSksDQogICAgICAgICAgICAgICAgICAgICAjIFdlJ2xsIHNldCB0aGUgYWdlIGdyb3VwIGd1aWRlIG9yZGVyIHRvIDENCiAgICAgICAgICAgICAgICAgICAgIGd1aWRlPWd1aWRlX2xlZ2VuZCh0aXRsZSA9ICJBZ2UgZ3JvdXAiLCBvcmRlcj0uLi4pKSArDQoNCiAgIyBTZXQgdGhlIGNvbG91ciBsZWdlbmQgDQogIHNjYWxlX2NvbG91cl9kaXNjcmV0ZShuYW1lID0gIkluZGljYXRvciIsIGxhYmVscyA9IGMoIiUgY2FzZXMiLCAiJSBob3NwaXRhbGl6YXRpb25zIikpICsNCg0KICAjIDQuIEdlb21zDQogICMgQWRkIGEgbGluZSB0byBjb25uZWN0IG91ciBhZ2UgZ3JvdXBzDQogIGdlb21fbGluZShhZXMoeD1hZ2VfZ3JvdXAsIHk9bWVhbiwgZ3JvdXAgPSBzdGF0X2dyb3VwLCBjb2xvdXI9c3RhdF9ncm91cCksIGxpbmV3aWR0aD0xKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgNS4yLjEgRm9yY2UgYW4gb3ZlcnJpZGUgdG8gdGhlIGxlZ2VuZCBhZXN0aGV0aWNzIHdpdGggYG92ZXJyaWRlLmFlc2ANCg0KQmVmb3JlIHdlIGxlYXZlIHRoZSBgZ3VpZGVzKClgIHNlY3Rpb24sIHdlIHNob3VsZCB1cGRhdGUgb3VyIHBsb3Qgb25lIGxhc3QgdGltZS4gV2hlbiB5b3UgYXJlIHdvcmtpbmcgd2l0aCBzbyBtYW55IHNoYXBlcywgdGhleSBjYW4gc29tZXRpbWVzIHNob3cgdXAgYSBsaXR0bGUgc21hbGxlciB0aGFuIHlvdSB3YW50LiBZb3UgbWF5IHdpc2ggdG8gaW5jcmVhc2UgdGhlaXIgc2l6ZSBvbiB0aGUgcGxvdCBidXQgdGhhdCBtYXkgZGlzcHJvcG9ydGlvbmF0ZWx5IGluY3JlYXNlIHRoZWlyIHNpemUgb24gdGhlIGxlZ2VuZC4gSWYgeW91IHRoaW5rIGFib3V0IHRoZSBsZWdlbmQgc2ltaWxhcmx5IHRvIGEgcGxvdCBpdHNlbGYsIHRoZW4geW91IGNhbiBncmFzcCBob3cgdGhlIGBvdmVycmlkZS5hZXNgIHBhcmFtZXRlciBtaWdodCB3b3JrLg0KDQpUbyBhZGp1c3Qgc29tZSBvZiB0aGUgYWVzdGhldGljIGVsZW1lbnRzIG9mIHlvdXIgcGxvdCBsZWdlbmQsIHByb3ZpZGUgYSBuYW1lZCBgbGlzdGAgdG8gdGhlIGBvdmVycmlkZS5hZXNgIHBhcmFtZXRlci4gWW91IGNhbiB1c2UgYWVzIHBhcmFtZXRlcnMgbGlrZSBgc2l6ZWAgYW5kIGBjb2xvdXJgIHRvIGFkanVzdCBob3cgeW91ciBsZWdlbmRzIGRpc3BsYXkgaW5mb3JtYXRpb24gcmF0aGVyIHRoYW4gZGV0ZXJtaW5pbmcgdGhlaXIgcGFyYW1ldGVycyBmcm9tIHRoZSBwbG90IGl0c2VsZi4gV2UnbGwgYmUgYXBwbHlpbmcgdGhpcyBwYXJhbWV0ZXIgd2l0aGluIG91ciBndWlkZXMuDQoNCkF0IHRoZSBzYW1lIHRpbWUsIHdlJ2xsIHVwZGF0ZSBvdXIgcG9pbnRzIHRvIGJlIGxhcmdlciBhbmQgYm9sZGVyL3RoaWNrZXIgYnkgYWx0ZXJpbmcgaXRzIGBzdHJva2VgIHBhcmFtZXRlci4NCg0KYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD0xMH0NCg0KY292aWRfZGVtb2dyYXBoaWNzLnBsb3QgKyANCiAgIyAyLiBBZXN0aGV0aWNzDQogICMgU2V0IG91ciBndWlkZSBwb3NpdGlvbnMgZm9yIGxpbmV0eXBlIGFuZCBjb2xvdXIgdG8gMg0KICBndWlkZXMobGluZXR5cGUgPSAibm9uZSIsDQogICAgICAgICBjb2xvdXIgPSBndWlkZV9sZWdlbmQodGl0bGU9IkluZGljYXRvciIsIG9yZGVyPTIpKSArDQoNCiAgIyAzLiBTY2FsaW5nDQogICMgcmVuYW1lIG91ciB4LWF4aXMgbGFiZWxzDQogIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzPWNvdmlkX2RlbW9ncmFwaGljc190b3RhbC5kZiRhZ2VfZ3JvdXAgJT4lIGxldmVscygpICU+JSBhcy5jaGFyYWN0ZXIoKSAlPiUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgVXNlIHN0cmluZyByZXBsYWNlbWVudCB0byBjaGFuZ2Ugb3VyIGxhYmVscw0KICAgICAgICAgICAgICAgICAgIHN0cl9yZXBsYWNlX2FsbChwYXR0ZXJuPSIgdG8gIiwgcmVwbGFjZW1lbnQgPSAiLSIpIA0KICAgICAgICAgICAgICAgICAgKSArDQoNCiAgIyBnZ3Bsb3Qgb25seSBhZGRzIDYgc2hhcGVzIGF1dG9tYXRpY2FsbHkgc28gd2UgbmVlZCB0byBhZGQgbW9yZSBtYW51YWxseQ0KICAjIyMgNS4yLjEgT3ZlcnJpZGUgdGhlIHNpemUgb2YgdGhlIHNoYXBlcyBpbiBvdXIgbGVnZW5kDQogICMgU2V0IHZhbHVlcyBiYXNlZCBvbiBudW1iZXIgb2YgbGV2ZWxzDQogIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXM9YygxOm5sZXZlbHMoY292aWRfZGVtb2dyYXBoaWNzX3RvdGFsLmRmJGFnZV9ncm91cCkpLCANCiAgICAgICAgICAgICAgICAgICAgICMgV2UnbGwgc2V0IHRoZSBhZ2UgZ3JvdXAgZ3VpZGUgb3JkZXIgdG8gMQ0KICAgICAgICAgICAgICAgICAgICAgZ3VpZGU9Z3VpZGVfbGVnZW5kKHRpdGxlID0gIkFnZSBncm91cCIsIG9yZGVyPTEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBJbmNyZWFzZSB0aGUgc2hhcGUgc2l6ZSBhbmQgbGluZSB0aGlja25lc3MNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAuLi4gPSBsaXN0KC4uLikpKSArDQoNCiAgIyBTZXQgdGhlIGNvbG91ciBsZWdlbmQgDQogIHNjYWxlX2NvbG91cl9kaXNjcmV0ZShuYW1lID0gIkluZGljYXRvciIsIGxhYmVscyA9IGMoIiUgY2FzZXMiLCAiJSBob3NwaXRhbGl6YXRpb25zIikpICsNCg0KICAjIDQuIEdlb21zDQogICMgQWRkIGEgbGluZSB0byBjb25uZWN0IG91ciBhZ2UgZ3JvdXBzDQogIGdlb21fbGluZShhZXMoeD1hZ2VfZ3JvdXAsIHk9bWVhbiwgZ3JvdXAgPSBzdGF0X2dyb3VwLCBjb2xvdXI9c3RhdF9ncm91cCksIGxpbmV3aWR0aD0xKSArDQoNCiAgIyMjIDUuMi4xIFVwZGF0ZSB0aGUgcG9pbnRzIHRvIGJlIGxhcmdlciBhbmQgdGhpY2tlciANCiAgZ2VvbV9wb2ludChhZXMoeT1tZWFuLCBncm91cCA9IHN0YXRfZ3JvdXAsIHNoYXBlPWFnZV9ncm91cCksIA0KICAgICAgICAgICAgIHNpemUgPSA2LCBzdHJva2UgPSAxLjUpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDUuMy4wIFRoZSBgZ2dmb3JjZWAgcGFja2FnZSBhbm5vdGF0ZXMgd2l0aCBzaW1wbGUgYGdlb21fbWFya18qKClgIG9wdGlvbnMNCg0KVGhlIGBnZ2ZvcmNlKClgIHBhY2thZ2UgYnJpbmdzIGhlbHBmdWwgZ2VvbXMgYW5kIGZ1bmN0aW9ucyB0byBgZ2dwbG90MmAgdGhhdCBjYW4gcXVpY2tseSBhbm5vdGF0ZSBncm91cHMgb2YgZGF0YSB3aXRoaW4geW91ciBwbG90cy4gVGhlc2UgbGF5ZXJzIHdvcmsgd2l0aCBgZ2dwbG90MmAgbGlrZSBvdGhlciBgZ2VvbV8qKClgIGxheWVycyBzbyB5b3UgY2FuIGFkZCB0aGVtIGludG8geW91ciBwbG90cyBxdWl0ZSBzaW1wbHkuIFRoZXNlIG9iamVjdHMgY2FuIGFsc28gYWNjZXB0IGFlc3RoZXRpY3MgbWFwcGluZ3MgKGluY2x1ZGluZyB0aGUgYWJpbGl0eSB0byBmaWx0ZXIgZ3JvdXBzKSBhbW9uZ3N0IG1hbnkgb3RoZXIgdGhlbWUtZXNxdWUgcGFyYW1ldGVycyBhbmQgYXJlIGFkZGVkIGluIGFuIGF1dG9tYXRlZCBmYXNoaW9uLiBNb3JlIGluZm9ybWF0aW9uIGNhbiBiZSBmb3VuZCBbaGVyZV0oaHR0cHM6Ly9nZ2ZvcmNlLmRhdGEtaW1hZ2luaXN0LmNvbS9yZWZlcmVuY2UvaW5kZXguaHRtbCkNCg0KfCBnZW9tICAgICAgICAgICAgICAgIHwgRGVzY3JpcHRpb24gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfDotLS0tLS0tLS0tLS0tLS0tLS0tLS0tfDotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwNCnwgZ2VvbV9tYXJrX2NpcmNsZSgpICB8IEFkZCBjaXJjbGVzIHRvIGFsbCBvZiB5b3VyIGRhdGEgZ3JvdXBzICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgZ2VvbV9tYXJrX3JlY3QoKSAgICB8IEFkZCByb3VuZGVkLWNvcm5lciByZWN0YW5nbGVzIHRvIHlvdXIgZGF0YSBncm91cHMgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgZ2VvbV9tYXJrX2VsbGlwc2UoKSB8IEFkZCBlbGxpcHNlcyB0byBhbGwgb2YgeW91ciBkYXRhIGdyb3VwcyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgZ2VvbV9tYXJrX2h1bGwoKSAgICB8IEFkZCBhIG1vcmUgdGlnaHRseS1maXR0ZWQgc2hhcGUvYmxvYiAoYWthIGh1bGwpIGFyb3VuZCB5b3VyIGRhdGEgZ3JvdXBzIHwNCg0KWW91IGNhbiBhbHNvIGFkZCBjdXN0b20gc2hhcGVzLCBzcGVjaWZ5aW5nIHRoZWlyIHR5cGUsIGxvY2F0aW9uLCBldGMuIGFuZCBleHRlbnNpb25zIHRvIHRoZSBgZmFjZXRfKigpYCBncm91cCBvZiBsYXllcnMgYWxsb3cgeW91IHRvIGZhY2V0IGJ5IGRpZmZlcmVudCBjb2x1bW5zLCB6b29tIGluIG9uIHBhcnQgb2YgYSBncmFwaCBhcyBhIGZhY2V0LCBhbmQgc3BsaXQgZmFjZXRzIGludG8gbXVsdGlwbGUgcGxvdHMuDQoNCkxldCdzIGFkZCBzb21lIGVsbGlwc2VzIHRvIG91ciBwbG90IGFuZCBleGNoYW5nZSBvdXIgYGdlb21fbGluZSgpYCBmb3IgYSBzbW9vdGhlciBgZ2VvbV9ic3BsaW5lKClgLiBNb3JlIGFib3V0IHRoZSBgZ2VvbV9ic3BsaW5lKClgIHBhcmFtZXRlcnMgY2FuIGJlIGZvdW5kIFtoZXJlXShodHRwczovL2dnZm9yY2UuZGF0YS1pbWFnaW5pc3QuY29tL3JlZmVyZW5jZS9nZW9tX2JzcGxpbmUuaHRtbCkNCg0KYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD0xMH0NCg0KZGVtb2dyYXBoaWNzX3N1bW1hcnkucGxvdCA8LQ0KICBjb3ZpZF9kZW1vZ3JhcGhpY3NfdG90YWwuZGYgJT4lIA0KICAjIFVuZ3JvdXAgdGhpcyBkYXRhZnJhbWUgdG8gY2xlYW4gaXQgdXAgYSBsaXR0bGUNCiAgdW5ncm91cCgpICU+JSANCiAgIyBGaWx0ZXIgZm9yIGN1bXVsYXRpdmUgZGF0YSBvbmx5DQogIGZpbHRlcihwZXJpb2QgPT0gImN1bXVsYXRpdmUiKSAlPiUgDQogICMgU2VsZWN0IGZvciBqdXN0IHRoZSBpbXBvcnRhbnQgY29sdW1ucw0KICBzZWxlY3QocHVibGljX2hlYWx0aF91bml0LCBhZ2VfZ3JvdXAsIHBlcmNlbnRfY2FzZXMsIHBlcmNlbnRfaG9zcGl0YWxpemF0aW9ucykgJT4lIA0KICAjIFBpdm90IHRoZSBtb2RpZmllZCB0YWJsZSB0byBjYXB0dXJlIHRoZSAic3RhdF9ncm91cCIgb2YgcGVyY2VudF9jYXNlcyB2cyBwZXJjZW50X2RlYXRocw0KICBwaXZvdF9sb25nZXIoY29scz1jKDMsNCksIG5hbWVzX3RvID0gInN0YXRfZ3JvdXAiLCB2YWx1ZXNfdG8gPSAicGVyY2VudF9QSFVfdG90YWwiKSAlPiUgDQogICMgR3JvdXAgdGhlIGRhdGEgYm90aCBieSBhZ2UgZ3JvdXAgYW5kIHRoZW4gc3RhdCBncm91cA0KICBncm91cF9ieShhZ2VfZ3JvdXAsIHN0YXRfZ3JvdXApICU+JSANCiAgIyBHZW5lcmF0ZSBzb21lIHN1bW1hcnkgc3RhdGlzdGljcw0KICBzdW1tYXJpc2UobWVhbiA9IG1lYW4ocGVyY2VudF9QSFVfdG90YWwpLCANCiAgICAgICAgICAgIHNkID0gc2QocGVyY2VudF9QSFVfdG90YWwpLA0KICAgICAgICAgICAgbWVkaWFuID0gbWVkaWFuKHBlcmNlbnRfUEhVX3RvdGFsKSkgJT4lICAgDQogIA0KICAjIFBsb3QgdGhlIGRhdGEgYXMgYSBtaXh0dXJlIG9mIG11bHRpcGxlIGdlb21zDQogIA0KICAjIDEuIERhdGENCiAgZ2dwbG90KC4pICsNCiAgICAjIDIuIEFlc3RoZXRpY3MNCiAgICBhZXMoeD1hZ2VfZ3JvdXAsIHkgPSBtZWFuLCBsaW5ldHlwZT1zdGF0X2dyb3VwKSArDQoNCiAgICAjIFRoZW1lcw0KICAgIHRoZW1lX21pbmltYWwoKSArDQoNCiAgICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MjApLCAjIHNldCB0ZXh0IHNpemUgdG8gMjANCiANCiAgICAgICAgICAjIFVwZGF0ZSB0aGUgcGFuZWwgdG8gZHJvcCB0aGUgbWlub3IgYXhpcyBncmlkIGxpbmVzDQogICAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICANCiAgICAgICAgICAjIFVzZSBhIGJsYWNrIGxpbmUgZm9yIHRoZSBheGVzDQogICAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIpLA0KICAgICAgICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChjb2xvdXIgPSAiYmxhY2siLCBmYWNlPSJib2xkIiksDQogICAgICAgICApICsNCg0KICAgICMgQWRkIGxhYmVscyB0byB0aGUgcGxvdA0KICAgIGxhYnModGl0bGUgPSAiUGVyY2VudCBjYXNlcyBhbmQgZGVhdGhzIGJ5IHByb3BvcnRpb24gcGVyIFBIVSBhY3Jvc3MgYWdlIGdyb3VwIiwNCiAgICAgICAgIHggPSAiXG5BZ2UgZ3JvdXAiLA0KICAgICAgICAgeSA9ICJQcm9wb3J0aW9uIG9mIHJlcG9ydGVkIFBIVSBkYXRhXG4iLA0KICAgICAgICAgY2FwdGlvbiA9ICJcbipBZ2UgZ3JvdXAgdmFsdWVzIGFyZSBjYWxjdWxhdGVkIGFzIGEgcGVyY2VudGFnZSBvZiB0b3RhbCBjYXNlcyBvciBkZWF0aHMgd2l0aGluIGEgUEhVIikgKw0KDQogICAgIyBTZXQgb3VyIGd1aWRlIHBvc2l0aW9ucyBmb3IgbGluZXR5cGUgYW5kIGNvbG91ciB0byAyDQogICAgZ3VpZGVzKGxpbmV0eXBlID0gIm5vbmUiLA0KICAgICAgICAgICBjb2xvdXIgPSBndWlkZV9sZWdlbmQodGl0bGU9IkluZGljYXRvciIsIG9yZGVyPTIpKSArDQoNCiAgICAjIDMuIFNjYWxpbmcNCiAgICAjIHJlbmFtZSBvdXIgeC1heGlzIGxhYmVscw0KICAgIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzPWNvdmlkX2RlbW9ncmFwaGljc190b3RhbC5kZiRhZ2VfZ3JvdXAgJT4lIGxldmVscygpICU+JSBhcy5jaGFyYWN0ZXIoKSAlPiUgICAgICANCiAgICAgICAgICAgICAgICAgICAgICMgVXNlIHN0cmluZyByZXBsYWNlbWVudCB0byBjaGFuZ2Ugb3VyIGxhYmVscw0KICAgICAgICAgICAgICAgICAgICAgc3RyX3JlcGxhY2VfYWxsKHBhdHRlcm49IiB0byAiLCByZXBsYWNlbWVudCA9ICItIikgDQogICAgICAgICAgICAgICAgICAgICkgKw0KDQogICAgIyBnZ3Bsb3Qgb25seSBhZGRzIDYgc2hhcGVzIGF1dG9tYXRpY2FsbHkgc28gd2UgbmVlZCB0byBhZGQgbW9yZSBtYW51YWxseQ0KICAgICMgT3ZlcnJpZGUgdGhlIHNpemUgb2YgdGhlIHNoYXBlcyBpbiBvdXIgbGVnZW5kDQogICAgIyBTZXQgdmFsdWVzIGJhc2VkIG9uIG51bWJlciBvZiBsZXZlbHMNCiAgICBzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzPWMoMTpubGV2ZWxzKGNvdmlkX2RlbW9ncmFwaGljc190b3RhbC5kZiRhZ2VfZ3JvdXApKSwgDQogICAgICAgICAgICAgICAgICAgICAgIGd1aWRlPWd1aWRlX2xlZ2VuZCh0aXRsZSA9ICJBZ2UgZ3JvdXAiLCBvcmRlcj0xLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3ZlcnJpZGUuYWVzID0gbGlzdChzaXplPTcsIHN0cm9rZSA9IDAuOCkpKSArDQoNCiAgICAjIFNldCB0aGUgY29sb3VyIGxlZ2VuZCANCiAgICBzY2FsZV9jb2xvdXJfZGlzY3JldGUobmFtZSA9ICJJbmRpY2F0b3IiLCBsYWJlbHMgPSBjKCIlIGNhc2VzIiwgIiUgaG9zcGl0YWxpemF0aW9ucyIpKSArDQoNCiAgICAjIDQuIERhdGENCiAgICAjIEFkZCBhbiBlcnJvcmJhciB0byByZXByZXNlbnQgdGhlIHN0YW5kYXJkIGRldmlhdGlvbiByYW5nZQ0KICAgIGdlb21fZXJyb3JiYXIod2lkdGggPSAwLjIsIGFlcyh5ID0gbWVhbiwgeW1pbiA9IG1lYW4tc2QsIHltYXggPSBtZWFuK3NkLCBjb2xvdXI9c3RhdF9ncm91cCksIHNpemU9MSkgKw0KDQogICAgIyBBZGQgYSBsaW5lIHRvIGNvbm5lY3Qgb3VyIGFnZSBncm91cHMNCiAgICAjIyMgNS4zLjAgcmVwbGFjZSBvdXIgbGluZSB3aXRoIGEgYmV6aWVyIGxpbmUgdGhhdCBpcyBhIGxpdHRsZSBzbW9vdGhlciBhbmQgZ29lcyB0aHJvdWdoIG1vc3Qgb2YgdGhlIHBvaW50cw0KICAgIC4uLihhZXMoZ3JvdXAgPSBzdGF0X2dyb3VwLCBjb2xvdXI9c3RhdF9ncm91cCksIHNpemU9MSkgKw0KDQogICAgIyMjIDUuMy4wIEFkZCBlbGxpcHNlcyB0byAyIHNwZWNpZmljIGFnZSBncm91cHMgdG8gaGlnaGxpZ2h0IHdoYXQgd2UgY2FyZSBhYm91dA0KICAgIC4uLihhZXMoZ3JvdXAgPSBhZ2VfZ3JvdXAsIGZpbHRlciA9IGFnZV9ncm91cCAlaW4lIGMoIjIwIHRvIDM5IiwgIjgwKyIpLCBsYWJlbD1hZ2VfZ3JvdXApLCANCiAgICAgICAgICAgICAgICAgICAgICBmaWxsPSJibHVlIiwgYWxwaGE9MC4yKSArDQoNCiAgICAjIFVwZGF0ZSB0aGUgcG9pbnRzIHRvIGJlIGxhcmdlciBhbmQgdGhpY2tlcg0KICAgIGdlb21fcG9pbnQoYWVzKHk9bWVhbiwgZ3JvdXAgPSBzdGF0X2dyb3VwLCBzaGFwZT1hZ2VfZ3JvdXApLCBzaXplID0gNiwgc3Ryb2tlID0gMS41KQ0KDQojIFNob3cgdGhlIHBsb3QNCmRlbW9ncmFwaGljc19zdW1tYXJ5LnBsb3QNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgNS40LjAgQWRkIGRhdGEgZnJvbSBvdGhlciBzb3VyY2VzIGRpcmVjdGx5IHRvIHlvdXIgYGdlb21fKigpYA0KDQpPbiBhIHNpZGUgbm90ZSB0byBhbm5vdGF0aW9uLCBzb21ldGltZXMgeW91IHdhbnQgdG8gYWRkIGEgbGl0dGxlIG1vcmUgaW5mb3JtYXRpb24gdG8geW91ciBwbG90LiBJbiBvdXIgY2FzZSBhYm92ZSwgd2UgaGF2ZSB0aGUgc3VtbWFyeSBkYXRhIGZyb20gb3VyIHBsb3QsIGJ1dCB3b3VsZG4ndCBpdCBiZSBuaWNlIHRvIGFkZCBzb21lIG9mIHRoZSAqYWN0dWFsKiBkYXRhIHBvaW50cyB0byB0aGUgdmlzdWFsaXphdGlvbj8NCg0KV2hpbGUgaXQgbWF5IG5vdCBiZSB0aGUgYmVzdCBjaG9pY2UgZm9yIHRoaXMgcGFydGljdWxhciBwbG90LCBpdCdzIHN0aWxsIHNvbWV0aGluZyB3ZSBjYW4gZG8gdG8gZGVtb25zdHJhdGUgdGhlIGltcG9ydGFuY2Ugb2YgbGF5ZXJpbmcgaW4gb3VyIGZpZ3VyZXMuIFdoaWxlIHdlIGhhdmVuJ3QgZXhwbGljaXRseSBkaXNjdXNzZWQgdGhpcywgaXQgc2hvdWxkIGJlIGNsZWFyIHRoYXQgYnkgZGVmYXVsdCwgZWFjaCBgZ2VvbV8qKClgIGRyYXdzIGl0cyBkYXRhIGZyb20gdGhlIGluaXRpYWwgZGF0YWZyYW1lIHByb3ZpZGVkIHRvIHRoZSBgZ2dwbG90KClgIGNhbGwuDQoNCk11Y2ggbGlrZSBtYXBwaW5nIGluZGl2aWR1YWwgYWVzdGhldGljcywgd2UgY2FuIGFsc28gYXNzaWduIGVhY2ggaW5kaXZpZHVhbCBgZ2VvbV8qKClgIGl0cyBvd24gZGF0YXNldCEgUmVjYWxsIHRoYXQgbGFzdCBsZWN0dXJlIHdlIGludHJvZHVjZWQgdGhlIGBnZ2JlZXN3YXJtYCBwYWNrYWdlLiBMZXQncyBhZGQgc29tZSBkYXRhcG9pbnRzIHRvIG91ciBsYXN0IHBsb3QgYnkgaW5jbHVkaW5nIGEgYGdlb21fcXVhc2lyYW5kb20oKWAgbGF5ZXIuIEluIG9yZGVyIHRvIGluY2x1ZGUgdGhpcyBkYXRhLCB3ZSBuZWVkIGFjdHVhbCBkYXRhIHBvaW50cyBzbyB3ZSdsbCBnZW5lcmF0ZSBhbiBpbnRlcm1lZGlhdGUgZGF0YWZyYW1lIGNhbGxlZCBgY292aWRfZGVtb19sb25nLmRmYC4NCg0KYGBge3J9DQojIEJ1aWxkIGEgbG9uZy1mb3JtYXQgZGF0YWZyYW1lIHRvIHN1cHBseSBsYXRlciB0byBvdXIgcGxvdA0KDQpjb3ZpZF9kZW1vX2xvbmcuZGYgPC0NCiAgY292aWRfZGVtb2dyYXBoaWNzX3RvdGFsLmRmICU+JSANCiAgIyBVbmdyb3VwIHRoaXMgZGF0YWZyYW1lIHRvIGNsZWFuIGl0IHVwIGEgbGl0dGxlDQogIHVuZ3JvdXAoKSAlPiUgDQogICMgRmlsdGVyIGZvciBjdW11bGF0aXZlIGRhdGENCiAgZmlsdGVyKHBlcmlvZCA9PSAiY3VtdWxhdGl2ZSIpICU+JSANCiAgIyBTZWxlY3QgZm9yIGp1c3QgdGhlIGltcG9ydGFudCBjb2x1bW5zDQogIHNlbGVjdChwdWJsaWNfaGVhbHRoX3VuaXQsIGFnZV9ncm91cCwgcGVyY2VudF9jYXNlcywgcGVyY2VudF9ob3NwaXRhbGl6YXRpb25zKSAlPiUgDQogICMgUGl2b3QgdGhlIG1vZGlmaWVkIHRhYmxlIHRvIGNhcHR1cmUgdGhlICJzdGF0X2dyb3VwIiBvZiBwZXJjZW50X2Nhc2VzIHZzIHBlcmNlbnRfZGVhdGhzDQogIHBpdm90X2xvbmdlcihjb2xzPWMoMyw0KSwgbmFtZXNfdG8gPSAic3RhdF9ncm91cCIsIHZhbHVlc190byA9ICJwZXJjZW50X1BIVV90b3RhbCIpDQoNCmhlYWQoY292aWRfZGVtb19sb25nLmRmKQ0KYGBgDQoNCmBgYHtyLCBmaWcud2lkdGg9MjAsIGZpZy5oZWlnaHQ9MTB9DQpkZW1vZ3JhcGhpY3Nfc3VtbWFyeS5wbG90ICsgDQogICAgIyMjIDUuNC4wIEFkZCBvdXIgcG9pbnRzIHVzaW5nIGJlZXN3YXJtIGZyb20gYSBESUZGRVJFTlQgZGF0YSBzZXQNCiAgICBnZW9tX3F1YXNpcmFuZG9tKGRhdGEgPSAuLi4sIA0KICAgICAgICAgICAgICAgICAgICAgYWVzKHg9YWdlX2dyb3VwLCB5ID0gcGVyY2VudF9QSFVfdG90YWwsIGdyb3VwID0gc3RhdF9ncm91cCksDQogICAgICAgICAgICAgICAgICAgICB2YXJ3aWR0aD1UUlVFLCBtZXRob2Q9InF1YXNpcmFuZG9tIiwgYWxwaGEgPSAwLjUpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDUuNS4wIFdvcmtpbmcgd2l0aCBzcGVjaWFsIGNoYXJhY3RlcnMgYW5kIHN5bWJvbHMgaW4geW91ciBwbG90IHRleHQNCg0KV29ya2luZyBpbiBiaW9sb2dpY2FsIHNjaWVuY2UsIHlvdSB3aWxsIG9mdGVuIGZpbmQgeW91cnNlbGYgd2FudGluZyB0byBpdGFsaWNpemUgc3BlY2llcyBuYW1lcyBvciBhZGQgc3BlY2lhbCBjaGFyYWN0ZXJzIHdoZW4gbmFtaW5nIHByb3RlaW5zIGV0Yy4gVGhpcyBpcyBub3QgYSBmZWF0IGVhc2lseSBhY2NvbXBsaXNoZWQgdXNpbmcgdGhlIG9wdGlvbnMgcHJvdmlkZWQgYnkgYGdncGxvdDJgLiBJbnN0ZWFkLCB5b3UgY2FuIGdlbmVyYXRlIHN0cmluZyBvYmplY3RzIHdpdGggdGhlIHJlcXVpcmVkIGZvbnQtY2hhbmdlcyBvciBzeW1ib2xzIGFuZCB0aGVuIHByb3ZpZGUgdGhlc2UgdG8gb2JqZWN0cyB0byB5b3VyIHBsb3QuIEluIGFkZGl0aW9uIHRvIHRoZXNlIHNwZWNpYWwgdGV4dCBvYmplY3RzLCB5b3UgY291bGQgYWxzbyBleHBsb3JlIHBhY2thZ2VzIHRoYXQgYWRkIHRoaXMga2luZCBvZiBmdW5jdGlvbmFsaXR5IG1vcmUgb3JnYW5pY2FsbHkgdG8geW91ciBwbG90cy4NCg0KIyMjIDUuNS4xIFVzZSB0aGUgYGV4cHJlc3Npb24oKWAgZnVuY3Rpb24gdG8gZ2VuZXJhdGUgYW4gZXhwcmVzc2lvbiBvYmplY3QNCg0KVGhlcmUgYXJlIGEgZmV3IHJvdXRlcyB0byBhY2NvbXBsaXNoIHRoaXMga2luZCBvZiBmb3JtYXR0aW5nLiBXZSdsbCBleHBsb3JlIHRoZSBmaXJzdCwgYGV4cHJlc3Npb24oKWAgd2hpY2ggbWFrZXMgYW4gZXhwcmVzc2lvbiBvYmplY3QuIFRoZSBgZXhwcmVzc2lvbigpYCBmdW5jdGlvbiBpbnRlcnByZXRzIGEgc2VyaWVzIG9mIHN0cmluZ3MgYW5kIGNoYXJhY3RlcnMgaW50byBhIG1hdGhlbWF0aWNhbGx5LWZvcm1hdHRlZCBleHByZXNzaW9uLiBXaGVuIHN1cHBsaWVkIGFzIGFuIGFyZ3VtZW50LCB0aGlzIG9iamVjdCBpcyBpbnRlcnByZXRlZCBhcyBhIG1hdGhlbWF0aWNhbCBleHByZXNzaW9uIGFuZCB0aGUgb3V0cHV0IGlzIGZvcm1hdHRlZCBiYXNlZCBvbiBhIFRlWC1saWtlIHNldCBvZiBydWxlcyB0aGF0IHBhcnNlIHRocm91Z2ggdGhlIHN5bnRheC4NCg0KV2l0aGluIHRoaXMgZnVuY3Rpb24sIHRoZXJlIGFyZSBhIG51bWJlciBvZiBwYXJhbWV0ZXJzIHRoYXQgY2FuICpzZWVtKiBsaWtlIGZ1bmN0aW9ucyBidXQgYXJlIGltcGxlbWVudGVkIHdpdGhpbiBgZXhwcmVzc2lvbigpYCByYXRoZXIgdGhhbiB1c2luZyB0aGUgYmFzZSBSIGZ1bmN0aW9ucyAtIHNvIGRvbid0IGV4cGVjdCB0aGUgc2FtZSBraW5kIG9mIGJlaGF2aW91cnMuIEhlcmUgaXMgYSBub24tZXhoYXVzdGl2ZSBsaXN0IG9mIHBvdGVudGlhbCBzaXR1YXRpb25zIHlvdSBtYXkgZW5jb3VudGVyLg0KDQp8IFN5bWJvbCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBEZXNjcmlwdGlvbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnw6LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXw6LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfA0KfCArLCAtLCAlXColLCAlLyUsICUrLSUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgYmFzaWMgbWF0aGVtYXRpY2FsIHN5bWJvbHMgZm9yICssIC0sIFwqLCAvLCBhbmQgJFxwbSQgICAgICAgICAgICAgICAgICB8DQp8IHBhc3RlKHgseSx6KSwgeFwqeVwqeiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBqdXh0YXBvc2UgeCwgeSwgYW5kIHogd2l0aG91dCBhbnkgc2VwYXJhdG9ycyAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgc3FydCh4KSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IHNxdWFyZSByb290IG9mIHggICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBzcXJ0KHgsIHkpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgdGhlIHl0aCByb290IG9mIHggICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IHBsYWluKHgpLCBib2xkKHgpLCBpdGFsaWMoeCksIGJvbGRpdGFsaWMoeCksIHN5bWJvbCh4KSwgdW5kZXJsaW5lKCkgfCBkcmF3IHggaW4gbm9ybWFsLCBib2xkLCBpdGFsaWMsIGJvbGRpdGFsaWMsIHN5bWJvbCBhbmQgdW5kZXJsaW5lZCBmb250IHwNCnwgbGlzdCh4LCB5LCB6KSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IG91dHB1dCBhIGNvbW1hLXNlcGFyYXRlZCBsaXN0IG9mIHgsIHksIHogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBoYXQoeCksIHRpbGRlKHgpLCBkb3QoeCksIGJhcih4KSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgYWRkIHN5bWJvbHMgYWJvdmUgeCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IGFscGhhIHRvIG9tZWdhLCBBbHBoYSB0byBPbWVnYSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBHcmVlayBzeW1ib2xzIGluIGxvd2VyIGFuZCB1cHBlciBjYXNlICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgaW5maW5pdHkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IHRoZSBpbmZpbml0eSBzeW1ib2wgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCB4IFx+IHksIHggXH5cfiB5ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgcHV0IGEgc3BhY2UgYmV0d2VlbiB4IGFuZCB5IG9yIHB1dCBleHRyYSBzcGFjZSBiZXR3ZWVuIHRoZW0gICAgICAgICAgICB8DQp8IHBoYW50b20oMCkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBsZWF2ZSBhIGdhcCBmb3IgIjAiIHdpdGhvdXQgZHJhd2luZyBpdCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgZnJhYyh4LCB5KSwgb3Zlcih4LCB5KSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IG91dHB1dCB4IG92ZXIgeSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBhdG9wICh4LCB5KSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgb3V0cHV0IHggb3ZlciB5IHdpdGhvdXQgYW55IGJhciAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQoNCk5vdGUgZnJvbSBhYm92ZSwgdG8gYnVpbGQgeW91ciBleHByZXNzaW9ucyBmcm9tIG11bHRpcGxlIHBhcnRzLCB5b3Ugc2hvdWxkIHVzZSB0aGUgXCogb3IgcGFzdGUoKSBvcGVyYXRvcnMgZnJvbSB3aXRoaW4gYGV4cHJlc3Npb24oKWAuDQoNCmBgYHtyLCBmaWcud2lkdGg9MjAsIGZpZy5oZWlnaHQ9MTB9DQoNCmRlbW9ncmFwaGljc19zdW1tYXJ5LnBsb3QgKyANCiAgDQogICMjIyA1LjUuMSBhbHRlciB0aXRsZSBsYWJlbHMgdXNpbmcgdGhlIGV4cHJlc3Npb24oKSBmdW5jdGlvbg0KICBsYWJzKHRpdGxlID0gLi4uLA0KICAgICAgIHggPSAiXG5BZ2UgZ3JvdXAiLA0KICAgICAgIHkgPSAiUHJvcG9ydGlvbiBvZiByZXBvcnRlZCBQSFUgZGF0YVxuIiwNCiAgICAgICBjb2xvdXIgPSAiUHVibGljIEhlYWx0aCBVbml0IiwNCiAgICAgICBjYXB0aW9uID0gLi4uDQogICAgICAgKSArDQoNCiAgIyBBZGQgb3VyIHBvaW50cyB1c2luZyBiZWVzd2FybSBmcm9tIGEgRElGRkVSRU5UIGRhdGEgc2V0DQogIGdlb21fcXVhc2lyYW5kb20oZGF0YSA9IGNvdmlkX2RlbW9fbG9uZy5kZiwgDQogICAgICAgICAgICAgICAgICAgYWVzKHg9YWdlX2dyb3VwLCB5ID0gcGVyY2VudF9QSFVfdG90YWwsIGdyb3VwID0gc3RhdF9ncm91cCksDQogICAgICAgICAgICAgICAgICAgdmFyd2lkdGg9VFJVRSwgbWV0aG9kPSJxdWFzaXJhbmRvbSIsIGFscGhhID0gMC41KQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgNS41LjIgRm9ybWF0IGFuZCBpbnRlcnByZXQgdmFyaWFibGVzIHVzaW5nIGBicXVvdGUoKWANCg0KVW5saWtlIHRoZSBgZXhwcmVzc2lvbigpYCBmdW5jdGlvbiwgdXNpbmcgYGJxdW90ZSgpYCBhbGxvd3MgeW91IHRvIHJlZmVyZW5jZSBpbmZvcm1hdGlvbiB3aGljaCBtYXkgYmUgc3RvcmVkIGluICoqKnZhcmlhYmxlcyoqKiBzbyB0aGF0IHlvdSBjYW4gYWRkIHRoZXNlIGluc3RlYWQgb2YgZXhwbGljaXRseSBpbmNsdWRpbmcgdGhlIHdvcmRzIHlvdSB3YW50LiBXaGVuIHRoaW5raW5nIGFib3V0IHVzaW5nIGBicXVvdGUoKWAgeW91IGNhbiBicmVhayB5b3VyIG1hdGggbm90YXRpb24gaW50byBmb3VyIGZvcm1zIG9mIHN5bnRheC4gVGhlc2Ugc2VjdGlvbnMgb3IgZm9ybXMgY2FuIGJlIGpvaW5lZCB3aXRoIHRoZSBcfiBzeW1ib2wuDQoNCnwgQ2xhc3Mgb2YgdGV4dCAgICB8IFN5bnRheCAgICAgICAgICAgICAgICAgICAgICB8IERlc2NyaXB0aW9uICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8Oi0tLS0tLS0tLS0tLS0tLS0tLXw6LS0tLS0tLS0tLS0tLS0tLS0tfDotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18DQp8IFN0cmluZ3MgICAgICAgICAgfCAibXkgdGV4dCIgXH4gICAgICAgICAgICAgICAgfCBXb3JkcyBhbmQgbm9uLW1hdGhlbWF0aWNhbCB0ZXh0IHRoYXQgeW91IHdhbnQgdG8gcHJpbnQgYXMtaXMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBNYXRoIEV4cHJlc3Npb25zIHwgaW5maW5pdHksIGFscGhhLCBmcmFjKHgsIHkpIHwgVW5xdW90ZWQgYW5kIGVzc2VudGlhbGx5IHRoZSBzYW1lIGtpbmRzIG9mIHN5bWJvbHMgdXNlYWJsZSBieSBgP3Bsb3RtYXRoYCBhbmQgYGV4cHJlc3Npb24oKWAuIHwNCnwgTnVtYmVycyAgICAgICAgICB8IDEsIDQyLCA5MDAwMDAgICAgICAgICAgICAgICB8IFVzZSB1bnF1b3RlZCB3aGVuIHBhcnQgb2YgbWF0aCBub3RhdGlvbi4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IFZhcmlhYmxlcyAgICAgICAgfCAqKi4oKip2YXJpYWJsZU5hbWUqKikqKiAgICAgfCBVc2VkIHRvIHBhc3MgaW4gYSBzdHJpbmcgb3IgbnVtZXJpYyBpbnRvIHlvdXIgZXF1YXRpb24uIE5vdGUgdGhlIHBlcmlvZCBhdCB0aGUgZnJvbnQhICAgICAgICAgfA0KDQpNYW55IFItZW50aHVzaWFzdHMgcHJlZmVyIHRoaXMgZm9ybSBvZiBnZW5lcmF0aW5nIGV4cHJlc3Npb25zIGZvciBpdCdzIGZsZXhpYmlsaXR5IHRvIGJ1aWxkIHdoYXRldmVyIHlvdSB3YW50Lg0KDQpgYGB7ciwgZmlnLndpZHRoPTIwLCBmaWcuaGVpZ2h0PTEwfQ0KDQojIEZpcnN0IGZpZ3VyZSBvdXQgdGhlIG1pbmltdW0gbnVtYmVyIG9mIHNhbXBsZXMgcGVyIGdyb3VwIHRvIGdlbmVyYXRlIGEgdmFyaWFibGUNCnNhbXBsZS5taW4gPC0NCiAgY292aWRfZGVtb19sb25nLmRmICU+JSANCiAgIyBHcm91cCB0aGUgZGF0YSBib3RoIGJ5IGFnZSBncm91cCBhbmQgdGhlbiBzdGF0IGdyb3VwDQogIGdyb3VwX2J5KGFnZV9ncm91cCwgc3RhdF9ncm91cCkgJT4lIA0KICAjIEdlbmVyYXRlIHRoZSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIHBlciBncm91cA0KICBzdW1tYXJpc2UoY291bnQgPSBuKCkpICU+JSANCiAgIyBDYWxjdWxhdGUgdGhlIG1pbmltdW0gc2FtcGxlIG51bWJlciBmcm9tIG91ciBkYXRhDQogIC4kY291bnQgJT4lIG1pbigpDQoNCiMgTm93IGJ1aWxkIHRoZSBwbG90DQpkZW1vZ3JhcGhpY3Nfc3VtbWFyeS5wbG90ICsgDQoNCiAgIyBhbHRlciB0aXRsZSBsYWJlbHMgdXNpbmcgdGhlIGV4cHJlc3Npb24oKSBmdW5jdGlvbg0KICBsYWJzKHRpdGxlID0gZXhwcmVzc2lvbigiRGlzdHJpYnV0aW9uIG9mIn5pdGFsaWMoIm5ldyBjYXNlcyIpfiJ2cyINCiAgICAgICAgICAgICAgICAgICAgICAgICAgfmJvbGQoImRlYXRocyIpfiJkdWUgdG8gQ09WSUQtMTkgYWNyb3NzIE9udGFyaW8gUEhVcyIpLA0KICAgICAgIHggPSAiXG5BZ2UgZ3JvdXAiLA0KICAgICAgIHkgPSAiUHJvcG9ydGlvbiBvZiByZXBvcnRlZCBQSFUgZGF0YVxuIiwNCiAgICAgICBjb2xvdXIgPSAiUHVibGljIEhlYWx0aCBVbml0IiwNCiAgICAgICANCiAgICAgICAjIyMgNS41LjIgYWx0ZXIgb3VyIGNhcHRpb24gdXNpbmcgdGhlIGJxdW90ZSgpIGZ1bmN0aW9uDQogICAgICAgY2FwdGlvbiA9IC4uLikgKw0KDQogICMgQWRkIG91ciBwb2ludHMgdXNpbmcgYmVlc3dhcm0gZnJvbSBhIERJRkZFUkVOVCBkYXRhIHNldA0KICBnZW9tX3F1YXNpcmFuZG9tKGRhdGEgPSBjb3ZpZF9kZW1vX2xvbmcuZGYsIA0KICAgICAgICAgICAgICAgICAgIGFlcyh4PWFnZV9ncm91cCwgeSA9IHBlcmNlbnRfUEhVX3RvdGFsLCBncm91cCA9IHN0YXRfZ3JvdXApLA0KICAgICAgICAgICAgICAgICAgIHZhcndpZHRoPVRSVUUsIG1ldGhvZD0icXVhc2lyYW5kb20iLCBhbHBoYSA9IDAuNSkNCmBgYA0KDQo6Ojogey5hbGVydCAuYWxlcnQtYmxvY2sgLmFsZXJ0LXdhcm5pbmd9DQoqKldhdGNoIG91dCBmb3Igc29tZSB0cmlja3kgc3ludGF4ISoqIEluIG91ciBhYm92ZSBleGFtcGxlLCB5b3UgbWF5IGhhdmUgbm90aWNlZCB0aGF0IHdlIGRpZCBub3QgdHJlYXQgdGhlICoqJSstJSoqIGxpa2UgYSBudW1iZXIgYnV0IHJhdGhlciB3ZSBwbGFjZWQgaXQgd2l0aGluICoqdHdvIHNldHMgb2Ygc2luZ2xlIHF1b3RlcyEqKiBGb3IgKnNvbWUqIG1hdGhwbG90IHN5bWJvbHMgdXNpbmcgdGhlICoqJXglKiogZm9ybWF0LCB5b3Ugd2lsbCBuZWVkIHRvIGZvbGxvdyB0aGlzIHJ1bGUgb2YgdGh1bWIuIEl0J3Mgbm90IHJlYWRpbHkgZm91bmQgaW4gYW55IGRvY3VtZW50YXRpb24gYnV0IGEgZGVlcCBzZWFyY2ggb2YgdGhlIGludGVybmV0IHdpbGwgeWllbGQgdGhpcyBzb2x1dGlvbiENCjo6Og0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDUuNS4zIFVzZSB0aGUgYGdndGV4dGAgcGFja2FnZSB0byBjcmVhdGUgc2ltcGxlIG1hcmtkb3duIGNvZGUNCg0KQXMgYW4gYWx0ZXJuYXRpdmUgbWV0aG9kIHRvIHByb2R1Y2Ugc2ltcGxlIGZvcm1hdHRpbmcgY2hhbmdlcyB0byB5b3VyIHRleHQsIHRoZSBgZ2d0ZXh0KClgIHBhY2thZ2UgcHJvdmlkZXMgaW1wcm92ZWQgdGV4dCByZW5kZXJpbmcgc3VwcG9ydCBmb3IgYGdncGxvdDJgLiBXaGlsZSB0aGlzIHBhY2thZ2Ugb25seSBzdXBwb3J0cyBhIGxpbWl0ZWQgc2V0IG9mIE1hcmtkb3duL0hUTUwvQ1NTIHN5bnRheCwgaXQgY2FuIGhhbmRsZSBzaW1wbGUgdGhpbmdzIGxpa2UgYm9sZCBhbmQgaXRhbGljIHRleHQsIGFzIHdlbGwgYXMgc3VwZXItIGFuZCBzdWJzY3JpcHRpbmcuDQoNClRoaXMgcGFja2FnZSBwcm92aWRlcyAyIG5ldyBgdGhlbWUoKWAgZWxlbWVudHM6DQoNCi0gICBgZWxlbWVudF9tYXJrZG93bigpYDogcmVuZGVycyB0ZXh0IGFzIG1hcmtkb3duL0hUTUwgd2l0aG91dCB3b3JkIHdyYXBwaW5nLg0KDQotICAgYGVsZW1lbnRfdGV4dGJveCgpYDogY3JlYXRlcyBhIG1hcmtkb3duL0hUTUwgdGV4dGJveCB3aXRoIHdvcmQgd3JhcHBpbmcuDQoNCkJvdGggb2YgdGhlc2UgZWxlbWVudHMgYXJlIG1lYW50IHRvIGVmZmVjdGl2ZWx5IHJlcGxhY2UgdGhlIGBlbGVtZW50X3RleHQoKWAgdGhhdCBpcyBuYXRpdmUgdG8gYGdncGxvdDJgLiBMZXQncyBhbHRlciB0aGUgeC0gYW5kIHktYXhpcyB0ZXh0IGEgbGl0dGxlIGJpdCB0byBzZWUgaG93IHRoaXMgd29ya3MuIFJlbWVtYmVyIHdlJ2xsIGhhdmUgdG8gcmVwbGFjZSBib3RoIG91ciBsYWJlbHMgYW5kIHVwZGF0ZSB0aGUgYHRoZW1lKClgIGVsZW1lbnRzIHdlIGFyZSBpbnRlcmVzdGVkIGluLg0KDQpNb3JlIGluZm9ybWF0aW9uIG9uIHRoZSBgZ2d0ZXh0YCBwYWNrYWdlIFtjYW4gYmUgZm91bmQgaGVyZV0oaHR0cHM6Ly9naXRodWIuY29tL3dpbGtlbGFiL2dndGV4dCkuIE5vdGUgdGhhdCB0aGlzIHBhY2thZ2UgaGFzIG5vdCBiZWVuIHVwZGF0ZWQgc2luY2UgSnVuZSAyMDIwIHNvICpjYXZlYXQgZW1wdG9yKi4NCg0KYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD0xMH0NCiMgTm93IGJ1aWxkIHRoZSBwbG90DQpkZW1vZ3JhcGhpY3Nfc3VtbWFyeS5wbG90ICsgDQoNCiAgIyBhbHRlciB0aXRsZSBsYWJlbHMgdXNpbmcgdGhlIGV4cHJlc3Npb24oKSBmdW5jdGlvbg0KICBsYWJzKHRpdGxlID0gZXhwcmVzc2lvbigiRGlzdHJpYnV0aW9uIG9mIn5pdGFsaWMoIm5ldyBjYXNlcyIpfiJ2cyINCiAgICAgICAgICAgICAgICAgICAgICAgICAgfmJvbGQoImRlYXRocyIpfiJkdWUgdG8gQ09WSUQtMTkgYWNyb3NzIE9udGFyaW8gUEhVcyIpLA0KICAgICAgIA0KICAgICAgICMjIyA1LjUuMyBhbHRlciBvdXIgY2FwdGlvbiB1c2luZyB0aGUgYnF1b3RlKCkgZnVuY3Rpb24NCiAgICAgICAuLi4gPSAiKioqQWdlKioqIGdyb3VwPHN1Yj5iaW5uZWQgd2hlbiByZXRyaWV2ZWQ8L3N1Yj4iLA0KICAgICAgIC4uLiA9ICJfUHJvcG9ydGlvbl8gX19vZl9fIDxzdXA+cmVwb3J0ZWQgPGk+UEhVPC9pPiBkYXRhPC9zdXA+IiwNCiAgICAgICANCiAgICAgICAjIGFsdGVyIG91ciBjYXB0aW9uIHVzaW5nIHRoZSBicXVvdGUoKSBmdW5jdGlvbg0KICAgICAgIGNhcHRpb24gPSBicXVvdGUoIkVycm9iYXJzIHJlcHJlc2VudCBtZWFuICJ+ICcnJSstJScnIH4ic3RhbmRhcmQgZGV2aWF0aW9uIHdpdGggbiJ+Ij49In4uKHNhbXBsZS5taW4pKSkgKw0KDQogICMjIyA1LjUuMyBDb252ZXJ0IHRoZSBwcm9wZXIgdGhlbWUgZWxlbWVudHMgdG8gbWFya2Rvd24NCiAgdGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF9tYXJrZG93bigpLA0KICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X21hcmtkb3duKCkpICsNCg0KICAjIEFkZCBvdXIgcG9pbnRzIHVzaW5nIGJlZXN3YXJtIGZyb20gYSBESUZGRVJFTlQgZGF0YSBzZXQNCiAgZ2VvbV9xdWFzaXJhbmRvbShkYXRhID0gY292aWRfZGVtb19sb25nLmRmLCANCiAgICAgICAgICAgICAgICAgICBhZXMoeD1hZ2VfZ3JvdXAsIHkgPSBwZXJjZW50X1BIVV90b3RhbCwgZ3JvdXAgPSBzdGF0X2dyb3VwKSwNCiAgICAgICAgICAgICAgICAgICB2YXJ3aWR0aD1UUlVFLCBtZXRob2Q9InF1YXNpcmFuZG9tIiwgYWxwaGEgPSAwLjUpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCjo6OiB7LmFsZXJ0IC5hbGVydC1ibG9jayAuYWxlcnQtc3VjY2Vzc30NCioqV2hpY2ggaXMgdGhlIGJlc3QgdGV4dCBtZXRob2QgZm9yIG1lPyoqIEFzIHlvdSBjYW4gc2VlIHRoZXJlIGFyZSBtYW55IHBhdGhzIHRvIGFjaGlldmUgc2ltaWxhciBnb2Fscy4gRGVwZW5kaW5nIG9uIHRoZSBjb21wbGV4aXR5IG9mIHlvdXIgbmVlZHMsIHlvdSBtYXkgY2hvb3NlIG9uZSBhcHByb2FjaCBvdmVyIGFub3RoZXIuIE92ZXJhbGwgKipicXVvdGUoKSoqIGlzIHBlcmhhcHMgdGhlIG1vc3QgY29tcGxleCB0byBsZWFybiBhbmQgbWFzdGVyIGJ1dCB0aGUgbW9zdCBmbGV4aWJsZSBzaW5jZSBpdCBjYW4gYWxzbyBwYXJzZSAqdmFyaWFibGVzKiBhcyBwYXJ0IG9mIGl0cyBzeW50YXguIElmIHlvdSBhcmUgZGVhbGluZyB3aXRoIHNpbXBsZSBtYXRoIGV4cHJlc3Npb25zLCB0aGVuIHRoZSAqKmV4cHJlc3Npb24oKSoqIGZ1bmN0aW9uIGNvdWxkIGJlIGZvciB5b3UuIFV0aWxpemluZyBhIHNpbXBsZXIgc3ludGF4LCBpdCBzdGlsbCBvZmZlcnMgYSBmYWlyIGFtb3VudCBvZiBmbGV4aWJpbGl0eSBmb3IgY3JlYXRpbmcgbWF0aGVtYXRpY2FsIGV4cHJlc3Npb25zLiBMYXN0bHksIGlmIHlvdSB3YW50IHRvIGRvIHNpbXBsZSBtb2RpZmljYXRpb25zIHRvIHRleHQgdGl0bGUgZm9ybWF0IHdpdGhvdXQgbXVjaCBuZWVkIGZvciBlcXVhdGlvbnMsIHRoZW4gKipnZ3RleHQqKiBtYXkgYmUgdGhlIHJvdXRlIHRvIGdvLg0KOjo6DQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyA1LjYuMCBNYXJnaW5hbCBwbG90cyB0byB2aXN1YWxpemUgcmVsYXRpb25zaGlwcyBhbmQgZGlzdHJpYnV0aW9ucyBmcm9tIGBnZ0V4dHJhYA0KDQpNYXJnaW5hbCBwbG90cyBhcmUgYSB2ZXJ5IHNwZWNpYWxpemVkIHBsb3QgdHlwZSBmcm9tIHRoZSBgZ2dFeHRyYWAgcGFja2FnZSB3aGljaCBjb21iaW5lcyBzY2F0dGVycGxvdCBkYXRhIHdpdGggZGlzdHJpYnV0aW9uIGRhdGEgaW4gdGhlIG1hcmdpbnMuIFRoZSBtYWluIHBsb3QgcGFuZWwgaGFzIHlvdXIgdHdvIHZhcmlhYmxlcyBhbG9uZyB0aGUgeCBhbmQgeSBheGlzLiBTZWNvbmRhcnkgcGxvdHMgYXJlIG1hZGUgb24gdGhlIG9wcG9zaXRlIG1hcmdpbnMgYW5kIGNhbiBiZSBpbiB0aGUgZm9ybSBvZiBkaXN0cmlidXRpb24tYmFzZWQgb2JqZWN0IGllLiwgaGlzdG9ncmFtcywgYm94cGxvdHMsIGV0Yy4NCg0KVGhlIHdvcmtob3JzZSBvZiB0aGlzIHBhY2thZ2UgaXMgdGhlIGBnZ01hcmdpbmFsKClgIGZ1bmN0aW9uIHdoaWNoIHRha2VzIGFzIGlucHV0IHBhcmFtZXRlcnM6DQoNCi0gICBgcGA6IHRoZSBnZ3Bsb3Qgb2JqZWN0IHlvdSB3b3VsZCBsaWtlIHRvIGFkZCB0bw0KDQotICAgYGRhdGFgOiBvcHRpb25hbCBhcyB0aGUgaW5mb3JtYXRpb24gY2FuIGJlIGRyYXduIGZyb20gcCwgb3RoZXJ3aXNlIGl0IGNhbiBiZSBhIGRhdGEuZnJhbWUgb2JqZWN0IG9mIG90aGVyIGRhdGENCg0KLSAgIGB4YDogdGhlIHZhcmlhYmxlIG5hbWUgYWxvbmcgdGhlIHgtYXhpcw0KDQotICAgYHlgOiB0aGUgdmFyaWFibGUgbmFtZSBhbG9uZyB0aGUgeS1heGlzDQoNCi0gICBgdHlwZWA6IHRoZSB0eXBlIG9mIG1hcmdpbmFsIHBsb3QgdG8gc2hvdyAtIGFjY2VwdGFibGUgdHlwZXMgYXJlIFtkZW5zaXR5LCBoaXN0b2dyYW0sIGJveHBsb3QsIHZpb2xpbiwgZGVuc2lncmFtIChoaXN0b2dyYW0vZGVuc2l0eSBwbG90IG92ZXJsYXkpXQ0KDQotICAgYG1hcmdpbnNgOiBhbG9uZyB3aGljaCBtYXJnaW5zIHRvIHNob3cgdGhlIHBsb3RzIC0gYWNjZXB0YWJsZSBpbnB1dHMgYXJlIFtib3RoLCB4LCB5XQ0KDQotICAgYHhwYXJhbXNgLCBgeXBhcmFtc2A6IGV4dHJhIHBhcmFtZXRlcnMgdG8gdXNlIG9ubHkgZm9yIHRoZSB4IG9yIHkgbWFyZ2luYWwgcGxvdHMNCg0KLSAgIGBncm91cENvbG91cmAsIGBncm91cEZpbGxgOiBpZiBgVFJVRWAsIHRoZSBjb2xvdXIgb3IgZmlsbCBvZiB0aGUgbWFyZ2luYWwgcGxvdHMgd2lsbCBiZSBtYXBwZWQgdG8gdGhlIGFlc3RoZXRpY3Mgb2YgdGhlIHNjYXR0ZXJwbG90DQoNCkxldCdzIHJlLWltYWdpbmUgb3VyIFBIVSBhZ2UgZ3JvdXAgZGF0YSBub3cgYXMgYSBzY2F0dGVycGxvdCB3aXRoIG1hcmdpbmFsIGJveHBsb3RzLiBXaGlsZSB0aGlzIHdvbid0IGJlIHRoZSBjbGVhcmVzdCB2aXN1YWxpemF0aW9uIG9mIHRoaXMga2luZCBvZiBkYXRhIGl0IHdpbGwgaGVscCB0byBkZW1vbnN0cmF0ZSBob3cgdG8gZ2VuZXJhdGUgbWFyZ2luYWwgcGxvdHMgd2l0aCB5b3VyIGRhdGEuDQoNCmBgYHtyLCBmaWcud2lkdGg9MjAsIGZpZy5oZWlnaHQ9MTB9DQoNCiMgQnVpbGQgb3VyIG1hcmdpbmFsIHBsb3QgZnJvbSB0aGUgd2lkZXItZm9ybWF0IHRoYXQgZGF0YSB3ZSBoYXZlDQpwaHVfYWdlX3NjYXR0ZXIucGxvdCA8LQ0KDQogIGNvdmlkX2RlbW9ncmFwaGljc190b3RhbC5kZiAlPiUgDQogIGZpbHRlcihhZ2VfZ3JvdXAgJWluJSBjKCIyMCB0byAzOSIsICI0MCB0byA1OSIsICI2MCB0byA3OSIsICI4MCsiKSwNCiAgICAgICAgcGVyaW9kID09ICJjdW11bGF0aXZlIikgJT4lIA0KICANCiAgIyAxLiBEYXRhDQogIGdncGxvdCguKSArDQogICAgIyAyLiBBZXN0aGV0aWNzDQogICAgYWVzKHg9cGVyY2VudF9jYXNlcywgeSA9IHBlcmNlbnRfaG9zcGl0YWxpemF0aW9ucywgY29sb3VyID0gYWdlX2dyb3VwKSArDQogICAgDQogICAgIyBUaGVtZXMNCiAgICB0aGVtZV9ncmV5KCkgKw0KICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwKSwgIyBzZXQgdGV4dCBzaXplDQogICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIgIyBNb3ZlIG91ciBsZWdlbmQgdG8gdGhlIGJvdHRvbQ0KICAgICAgICAgKSArIA0KICAgICMgVXBkYXRlIHRoZSBsZWdlbmQgc28gdGhhdCB0aGUgbGVnZW5kIGtleXMgYXJlIGxhcmdlcg0KICAgIGd1aWRlcyhjb2xvdXI9Z3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcz0gbGlzdChzaXplPTQpKSkgKw0KDQogICAgIyBVcGRhdGUgdGhlIGxhYmVscw0KICAgIGxhYnMoeCA9ICJQZXJjZW50IGNhc2VzIiwNCiAgICAgICAgIHkgPSAiUGVyY2VudCBob3NwaXRhbGl6YXRpb25zIiwNCiAgICAgICAgIGNvbG91ciA9ICJBZ2UgZ3JvdXAiKSArDQoNCiAgICAjIDMuIFNjYWxpbmcNCiAgICBzY2FsZV9jb2xvdXJfdmlyaWRpc19kKG9wdGlvbiA9ICJ2aXJpZGlzIikgKw0KDQogICAgIyA0LiBHZW9tcw0KICAgIC4uLihzaXplID0gNCwgYWxwaGEgPSAwLjgpICMgQWRkIG91ciBkYXRhIHBvaW50cw0KDQojIEFkZCBvdXIgbWFyZ2luYWwgYm94cGxvdHMgdG8gb3VyIGdyYXBoDQpwaHVfbWFyZ2luYWwucGxvdCA8LSBnZ01hcmdpbmFsKC4uLiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGU9Li4uLCBncm91cEZpbGw9VFJVRSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1hcmdpbnM9ImJvdGgiLCBzaXplPTUpDQoNCiMgcGxvdCBvdXIgbWFyZ2luYWwgcGxvdA0KcGh1X21hcmdpbmFsLnBsb3QNCmBgYA0KDQo6Ojogey5hbGVydCAuYWxlcnQtYmxvY2sgLmFsZXJ0LXdhcm5pbmd9DQoqKlBhY2thZ2VzIG9mIGNvbnZlbmllbmNlIG1heSBjb21lIGF0IGEgY29zdDoqKiBXaGlsZSBhIHBhY2thZ2UgbGlrZSAqKmdnRXh0cmEqKiBwcm92aWRlcyBhIGNvbnZlbmllbnQgd2F5IHRvIHByb2R1Y2UgbWFyZ2luYWwgcGxvdHMsIGl0IGlzIGEgcHJlLXBhY2thZ2VkIGZ1bmN0aW9uIHRoYXQgY2FuIGJlIGEgbGl0dGxlIGxpbWl0ZWQuIElmIHVzZWQgY29ycmVjdGx5LCB5b3UgY2FuIG1ha2UgeW91ciBiYXNlIHBsb3Qgd2l0aCBhbGwgdGhlIGNoYW5nZXMgeW91IG5lZWQgYW5kIHRoZW4gYWRkIHlvdXIgY2hvaWNlIG9mIHRoZSBhdmFpbGFibGUgbWFyZ2luYWwgcGxvdHMuIEl0IHNob3VsZCBtYWtlIGEgZmFpcmx5IGdvb2QgdmlzdWFsaXphdGlvbiBmb3IgbG93IGVmZm9ydCBhcyBsb25nIGFzIHlvdSdyZSBoYXBweSB3aXRoIGl0cyByZXN1bHRzLiBBbHNvLCB0aGlzIHBhY2thZ2UgaGFzbid0IGhhZCBhIG1ham9yIHVwZGF0ZSBzaW5jZSAyMDE4IGFsdGhvdWdoIHNtYWxsIHVwZGF0ZXMgYW5kIGJ1ZyBmaXhlcyBhcHBlYXIgdG8gYmUgZ2VuZXJhdGVkIGJ5IHRoZSBjcmVhdG9yLiBGb3IgbW9yZSBpbmZvcm1hdGlvbiwgeW91IGNhbiBjaGVjayBvdXQgbW9yZSBhdCB0aGUgW2dnRXh0cmEgY3JhbiBob21lcGFnZV0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL2dnRXh0cmEvdmlnbmV0dGVzL2dnRXh0cmEuaHRtbCkgb3IgW2dvIHRvIHRoZSBnZ0V4dHJhIEdpdEh1YiBwYWdlXShodHRwczovL2dpdGh1Yi5jb20vZGFhdHRhbGkvZ2dFeHRyYSkuDQo6OjoNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMgNi4wLjAgVGFraW5nIGl0IHVwIGEgbm90Y2gNCg0KVGhlcmUgYXJlIG1hbnkgZmFudGFzdGljIFIgcGFja2FnZXMgdG8gYW5hbHl6ZSBhbmQgdmlzdWFsaXplIHlvdXIgZGF0YS4gQXMgYSBncm91cCwgd2UgYXJlIGxpa2VseSB3b3JraW5nIGluIGEgdmFyaWV0eSBvZiBzcGVjaWFsaXplZCBhcmVhcy4gVGhlIHBsb3RzIHdlIGhhdmUgbWFkZSBzbyBmYXIgdG9kYXkgc2hvdWxkIGJlIHVzZWZ1bCBmb3IgZGF0YSBleHBsb3JhdGlvbiBmb3IgbWFueSBkaWZmZXJlbnQga2luZHMgb2YgZGF0YS4gSW4gdGhpcyBmaW5hbCBzZWN0aW9uIHdlIGFyZSBnb2luZyB0byBsZWFybiBob3cgdG8gYXJyYW5nZSBtdWx0aXBsZSBwbG90cyBwZXIgcGFnZSBmb3IgdGhvc2UgcHVibGljYXRpb24tcmVhZHkgZmlndXJlcy4NCg0KIyMgNi4xLjAgTXVsdGlwbGUgcGxvdHMgb24gb25lIHBhZ2UgKGllLiBmb3IgcHVibGljYXRpb24gaW1hZ2VzKSB3aXRoIGBnZ2FycmFuZ2UoKWANCg0KVGhlcmUgYXJlIGEgdmFyaWV0eSBvZiBtZXRob2RzIHRvIG1peCBtdWx0aXBsZSBncmFwaHMgb24gdGhlIHNhbWUgcGFnZSwgaG93ZXZlciBgZ2dwbG90MmAgZG9lcyBub3Qgd29yayB3ZWxsIHdpdGggYWxsIG9mIHRoZW0uIEkgYW0gZ29pbmcgdG8gd29yayB3aXRoIGEgcGFja2FnZSBiYXNlIHRoYXQgdXNlcyBgZ3JpZEV4dHJhYCAod2hpY2ggYWxsb3dzIHVzIHRvIGFycmFuZ2UgcGxvdHMpIGJ1dCB3b3JrcyB3ZWxsIHdpdGggYGdncGxvdDJgIGNhbGxlZCBgZ2dwdWJyYCAod2hpY2ggYWxsb3dzIHVzIHRvIGFsaWduIHRoZSBheGVzIG9mIG91ciBwbG90cykuIEZvciBhIGRlbW9uc3RyYXRpb24sIHdlIGFyZSBnb2luZyB0byB0YWtlIDMgcGxvdHMgdGhhdCB3ZSBtYWRlIGVhcmxpZXIgKGBwaHVfY2FzZXMucGxvdGAsIGBkZW1vZ3JhcGhpY3MucGxvdGAsIGBwaHVfbWFyZ2luYWwucGxvdGApIGFuZCB0aGVuIGFycmFuZ2UgYW5kIGFsaWduIHRoZW0gaW4gdGhlIHNhbWUgZmlndXJlLiAoPGh0dHA6Ly93d3cuc3RoZGEuY29tL2VuZ2xpc2gvcnBrZ3MvZ2dwdWJyLz4pDQoNCjo6OiB7YWxpZ249ImNlbnRlciJ9DQo8aW1nIHNyYz0iaHR0cHM6Ly9naXRodWIuY29tL2NhbW9rL0NTQl9Db3Vyc2VfTWF0ZXJpYWxzL2Jsb2IvbWFpbi9BZHZWaXovZ2dhcnJhbmdlX2V4YW1wbGVzLnBuZz9yYXc9dHJ1ZSIgd2lkdGg9IjcwMCIvPg0KDQpFeGFtcGxlIHBsb3QgYXJyYW5nZW1lbnRzIHRoYXQgY2FuIGJlIGFjY29tcGxpc2hlZCB3aXRoIHRoZSBgZ2dwdWJyYCBwYWNrYWdlLg0KOjo6DQoNCmBnZ2FycmFuZ2UoKWAgaXMgYSBmdW5jdGlvbiB0aGF0IHRha2VzIHlvdXIgcGxvdHMsIHRoZWlyIGxhYmVscywgYW5kIGhvdyB5b3Ugd291bGQgbGlrZSB5b3VyIHBsb3RzIGFycmFuZ2VkIGluIHJvd3MgYW5kIGNvbHVtbnMuIFRvIHN0YXJ0IGxldCdzIHB1dCBvdXIgUEhVIGNhc2UgZGF0YSAoYHBodV9jYXNlcy5wbG90YCkgYWJvdmUgb3VyIFBIVSBhZ2UgZ3JvdXAgZGF0YSAoYHBodV9hZ2UucGxvdGApLiBJZiB5b3UgcGljdHVyZSBlYWNoIHBsb3QgYXMgYSBzcXVhcmUgaW4gYSBncmlkLCB3ZSBuZWVkIG9uZSBjb2x1bW4gKG9uZSBmb3IgZWFjaCBwbG90LCBgbmNvbCA9IDFgKSBhbmQgdHdvIHJvd3MgKHNpbmNlIHRoZXkgYXJlIHN0YWNrZWQsIGBucm93ID0gMmApLg0KDQpgYGB7ciwgZmlnLndpZHRoPTIwLCBmaWcuaGVpZ2h0PTIwfQ0KDQojIEFycmFuZ2UgdGhlIHR3byBwbG90cyBpbiBhIHNpbmdsZSBwYWdlICAgIA0KZ2dhcnJhbmdlKC4uLiwgLi4uLCANCiAgICAgICAgICBsYWJlbHMgPSBjKCJBIiwgIkIiKSwNCiAgICAgICAgICBuY29sID0gLi4uLCBucm93ID0gLi4uKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyA2LjIuMCBBcnJhbmdlIHBsb3RzIHdpdGhpbiBwbG90cw0KDQpOZXh0IHdlIHdpbGwgYWRkIGluIHRoZSBib3hwbG90IGJ5IG5lc3RpbmcgYSBgZ2dhcnJhbmdlKClgIGNhbGwgd2l0aGluIGFub3RoZXIuDQoNCkltYWdpbmUgYSBzcXVhcmUgd2l0aCA0IGJveGVzLg0KDQoxXC4gV2UgYXJlIGdvaW5nIHRvIHBsYWNlIG91ciBsaW5lIGdyYXBoIGFjcm9zcyB0aGUgdG9wIHJvdyAodG9wIDIgYm94ZXMpDQoNCjJcLiBXZSdsbCBwbGFjZSBvdXIgYWdlIGdyb3VwIGRhdGEgaW4gdGhlIGJvdHRvbSBsZWZ0IGJveA0KDQozXC4gV2UnbGwgZHJvcCBvdXIgbWFyZ2luYWwgcGxvdCBpbnRvIHRoZSBib3R0b20gcmlnaHQgYm94DQoNClRvIGRvIHRoaXMsIHdlIGFyZSBhcnJhbmdpbmcgMiByb3dzIChvbmUgd2l0aCB0aGUgbGluZSBncmFwaCBhbmQgb25lIHdpdGggdGhlIFsqKmFnZSBncm91cCArIG1hcmdpbmFsIHBsb3QqKl0sIGBucm93ID0gMmApIGFuZCB3ZSBhcmUgYXJyYW5naW5nIDIgY29sdW1ucyBpbiB0aGUgYm90dG9tIHJvdyAob25lIHdpdGggdGhlIGFnZSBncm91cCBhbmQgb25lIHdpdGggdGhlIG1hcmdpbmFsIHBsb3QsIGBuY29sID0gMmApLg0KDQpgYGB7ciwgZmlnLndpZHRoPTIwLCBmaWcuaGVpZ2h0PTIwfQ0KDQojIEFycmFuZ2UgdGhlIHR3byBwbG90cyBpbiBhIHNpbmdsZSBwYWdlICAgIA0KZ2dhcnJhbmdlKHBodV9jYXNlcy5wbG90LCAjIHJvdyAxIHBsb3QNCiAgICAgICAgICAjIHJvdyAyIHBsb3RzDQogICAgICAgICAgZ2dhcnJhbmdlKC4uLiwgLi4uLA0KICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJCIiwgIkMiKSwNCiAgICAgICAgICAgICAgICAgICAgbmNvbCA9IDIsIA0KICAgICAgICAgICAgICAgICAgICBucm93ID0gMQ0KICAgICAgICAgICAgICAgICAgICksDQogICAgICAgICAgIyBmaW5pc2ggc3BlY2lmeWluZyBjaGFyYWN0ZXJpc3RpY3Mgb2YgdGhlIHR3by1yb3cgYXJyYW5nZW1lbnQNCiAgICAgICAgICBsYWJlbHMgPSBjKCJBIiksDQogICAgICAgICAgbmNvbCA9IDEsIA0KICAgICAgICAgIG5yb3cgPSAyDQogICAgICAgICApDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDYuMy4wIFNtYWxsIGNoYW5nZXMgY2FuIGJlIG1hZGUgd2l0aCBgYWxpZ25gIGFuZCBgZm9udCgpYA0KDQpPa2F5LCB0aGVyZSBhcmUgYSBmZXcgcHJvYmxlbXMgd2l0aCB0aGlzIGFycmFuZ2VtZW50Lg0KDQoqKlByb2JsZW0gMSoqOiBTcGFjaW5nIGFzaWRlLCBvdXIgdGl0bGUgaW4gcGxvdCBCIGhhcyBzcHJlYWQgb3ZlciBpbnRvIGFyZWEgQy4gSWYgeW91IHdhbnRlZCB0byBrZWVwIGl0LCB5b3Ugd291bGQgaGF2ZSB0byBmaXggdXAgdGhlIHRleHQgaW4gdGhlIHBsb3QgYW5kIHRyeSBhZ2Fpbi4gSG93ZXZlciwgd2UgY2FuIHRyZWF0IHRoZSBwbG90cyBtdWNoIGxpa2UgdGhlaXIgb3duIGRhdGEgYW5kIGtlZXAgYWx0ZXJpbmcgdGhlbSB3aXRoIHRoZSBgK2Agc3ltYm9sLiBUaGF0IG1lYW5zIGZvciBhIHF1aWNrIGZpeCwgd2UgKmNvdWxkKiBqdXN0IHJlbW92ZSB0aGUgdGl0bGUgYWx0b2dldGhlci4gRG8geW91IHJlbWVtYmVyIGhvdyB0byBhY2Nlc3MgdGhlIHBsb3QgdGl0bGU/DQoNCioqUHJvYmxlbSAyKio6IHRoZSB4LWF4ZXMgaW4gb3VyIEIvQyBwbG90cyBkb24ndCBsaW5lIHVwIHdlbGwuIFdvdWxkIGl0IGxvb2sgYmV0dGVyIGlmIHRoZXkgZGlkPyBJZiB5LWF4aXMgbGluZXMgb3IgeC1heGlzIGxpbmVzIGFyZSBub3QgYWxpZ25lZCwgdGhpcyBjYW4gYmUgZml4ZWQgd2l0aCBhIGNhbGwgdG8gYGFsaWduID0gInYiYCBvciBgYWxpZ249ImgiYC4NCg0KKipQcm9ibGVtIDMqKjogdGhlIGZvbnQgbGFiZWxzIGRlbm90aW5nIGVhY2ggcGxvdCBsb29rIGEgbGl0dGxlIHNtYWxsIG92ZXJhbGwuIFdlIGNhbiBjaGFuZ2UgdGhpcyBhc3BlY3Qgd2l0aCB0aGUgYGZvbnQubGFiZWxzYCBwYXJhbWV0ZXIuDQoNCklmIHlvdSB3YW50ZWQgdG8gbWFrZSBzdXJlIGFsbCBheGlzIHRpdGxlcyBhcmUgdGhlIHNhbWUgc2l6ZSB5b3UgY2FuIHNwZWNpZnkgdGhlc2Ugc21hbGwgY2hhbmdlcyB1c2luZyBgZm9udCgpYC4gWW91IGNhbiB0cnkgdG8gYWNjZXNzIHRoZXNlIGF0dHJpYnV0ZXMgdGhyb3VnaCBzaW1wbGUgbmFtZXMgbGlrZSAiYXhpcy50aXRsZSIsIGFuZCAibGVnZW5kLnRpdGxlIiBpZSBgZm9udCgiYXhpcy50aXRsZSIsIHNpemU9OSlgIGJ1dCB5b3UgbmVlZCB0byBzZXQgKioqZWFjaCBncmFwaCBhbmQgZWFjaCBhdHRyaWJ1dGUgc2VwYXJhdGVseSoqKi4NCg0KTGV0J3MgZHJvcCBvdXIgcGxvdCBCIHRpdGxlLCBhbmQgdHJ5IHRvIHNob3JlIHVwIHRoZSBheGVzIGJldHdlZW4gQiBhbmQgQy4gVW5mb3J0dW5hdGVseSB3ZSBtYXkgYmUgc3RvcHBlZCBieSB0aGUgY3Jvd2RlZCBzcGFjaW5nIGF0IHRoZSBib3R0b20gb2YgdGhlc2UgcGxvdHMuDQoNCmBgYHtyLCBmaWcud2lkdGg9MjAsIGZpZy5oZWlnaHQ9MjB9DQoNCnBsb3QgPC0NCg0KIyBBcnJhbmdlIHRoZSB0d28gcGxvdHMgaW4gYSBzaW5nbGUgcGFnZSAgICANCmdnYXJyYW5nZShwaHVfY2FzZXMucGxvdCwgDQogICAgICAgICAgZ2dhcnJhbmdlKGRlbW9ncmFwaGljcy5wbG90ICsgLi4uLCAjIyMgNi4zLjAgcmVtb3ZlIHRoZSB0aXRsZQ0KICAgICAgICAgICAgICAgICAgICBwaHVfbWFyZ2luYWwucGxvdCwNCiAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiQiIsICJDIiksDQogICAgICAgICAgICAgICAgICAgIG5jb2wgPSAyLCANCiAgICAgICAgICAgICAgICAgICAgbnJvdyA9IDEsDQogICAgICAgICAgICAgICAgICAgIC4uLiA9IGxpc3Qoc2l6ZT0yMCksICMgbWFrZSB0aGUgbGFiZWxzIGxhcmdlcg0KICAgICAgICAgICAgICAgICAgICBhbGlnbiA9ICJoIiAjIFRyeSB0byBhbGlnbiB0aGUgeC1heGlzIG9mIGJvdGggcGxvdHMNCiAgICAgICAgICAgICAgICAgICApLA0KICAgICAgICAgIGxhYmVscyA9IGMoIkEiKSwNCiAgICAgICAgICBuY29sID0gMSwgDQogICAgICAgICAgbnJvdyA9IDIsIA0KICAgICAgICAgIC4uLiA9IGxpc3Qoc2l6ZT0yMCkgIyBNYXRjaCB0aGUgaW5jcmVhc2VkIGxhYmVsIHNpemUgb2YgdGhlIG90aGVyIHBsb3RzDQogICAgICAgICApDQpwbG90IA0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyA2LjQuMCBEZXRlcm1pbmUgc2lnbmlmaWNhbmNlIGxldmVscyBmb3IgeW91ciBwbG90cyB3aXRoIGBnZ3B1YnJgDQoNCk9uZSBsYXN0IHRvb2wgdGhhdCB5b3UgbWlnaHQgZmluZCB1c2VmdWwgaW4geW91ciBwbG90cyBpcyB0aGUgYWRkaXRpb24gb2Ygc2lnbmlmaWNhbmNlIGxldmVscyBvciBwLXZhbHVlcyB0byB5b3VyIHBsb3RzLiBTaW5jZSB3ZSd2ZSBhbHJlYWR5IGxvYWRlZCB0aGUgYGdncHVicmAgcGFja2FnZSwgd2UnbGwgdXNlIGEgZnVuY3Rpb24gZm9yIHBhaXItd2lzZSBjb21wYXJpc29ucyBjYWxsZWQgYHN0YXRfcHdjKClgIHdoaWNoIHdpbGwgYWxsb3cgdXMgdG8gcGVyZm9ybSBhIGxpbWl0ZWQgYW5hbHlzaXMgb2Ygb3VyIGRhdGEuDQoNCkJlZm9yZSBjb250aW51aW5nLCB3ZSBzaG91bGQgdGFrZSBhIGxvb2sgYXQgdGhlIGBjb21wYXJlX21lYW5zKClgIGZ1bmN0aW9uIHRvIHNlZSBob3cgYGdncHVicmAgcGVyZm9ybXMgaXRzIGFuYWx5c2VzLiBUaGlzIGZ1bmN0aW9uLCBsaWtlIG90aGVyIG1vZGVsaW5nIGZ1bmN0aW9ucyAoZWcgdGhpbmsgYGxtKClgKSBjYW4gYWNjZXB0IGEgZm9ybXVsYSBiYXNlZCBvbiB5b3VyIHZhcmlhYmxlcyBmcm9tIGEgc3BlY2lmaWMgc2V0IG9mIGRhdGEuIEluIG91ciBjYXNlLCB3ZSdkIGxpa2UgdG8gc2VlIGhvdywgd2l0aGluIGVhY2ggYWdlIGdyb3VwLCB0aGUgcGVyY2VudCBjYXNlcyBjb21wYXJlcyB0byB0aGUgcGVyY2VudCBob3NwaXRhbGl6YXRpb25zLg0KDQpUaGUgYGNvbXBhcmVfbWVhbnMoKWAgZnVuY3Rpb25zIGhhcyBhIGZldyByZWxldmFudCBwYXJhbWV0ZXJzIHRvIGhlbHAgdXMgb3V0Og0KDQotICAgYGZvcm11bGFgOiB0aGUgZm9ybXVsYSB3ZSB1c2UgdG8gZGVmaW5lIG91ciBkZXBlbmRlbnQgdmFyaWFibGUgYXMgYSBmdW5jdGlvbiBvZiBvdXIgaW5kZXBlbmRlbnQNCg0KLSAgIGBkYXRhYDogdGhlIGRhdGEgc2V0IHlvdSB3aWxsIGJlIHVzaW5nDQoNCi0gICBgbWV0aG9kYDogdGhlIHR5cGUgb2YgY29tcGFyaXNvbnMgeW91J2QgbGlrZSB0byBtYWtlIGFzIGVpdGhlciBjb21wYXJpbmcgbWVhbnMgZGlyZWN0bHkgKGB0LnRlc3RgIG9yIGB3aWxjb3gudGVzdGApIHZzIG9tbmlidXMgdGVzdHMgKGBhbm92YWAgb3IgYGtydXNrYWwudGVzdGApLg0KDQotICAgYHJlZi5ncm91cGA6IGEgY2hhcmFjdGVyIHN0cmluZyBvciBudW1lcmljIHZhbHVlIGRlbm90aW5nIHdoaWNoIGdyb3VwIHRoZSBvdGhlciBjb21wYXJpc29ucyBhcmUgdG8gYmUgbWFkZSBhZ2FpbnN0ICh0aGluayBpbiB0ZXJtcyBvZiBhIGNvbnRyb2wgZ3JvdXAhKQ0KDQotICAgYGdyb3VwLmJ5YDogYSBjaGFyYWN0ZXIgdmVjdG9yIHN0YXRpbmcgd2hpY2ggYWRkaXRpb25hbCB2YXJpYWJsZXMgeW91J2QgbGlrZSB0byB1c2UgaW4gZ3JvdXBpbmcgeW91ciBkYXRhLiBUaGlzIGlzIHVzZWQgZm9yIGdyb3VwZWQgcGxvdHMhDQoNCi0gICBgcC5hZGp1c3QubWV0aG9kYDogaG93IHlvdSdkIGxpa2UgdG8gY29ycmVjdCBmb3IgbXVsdGlwbGUgY29tcGFyaXNvbnMgKGVnLiBib25mZXJyb25pLCBob21tZWwsIGhvY2hiZXJnLCBCSCwgZXRjKQ0KDQpMZXQncyB0cnkgb3V0IHRoZSBgY29tcGFyZV9tZWFucygpYCBmdW5jdGlvbiBvbiBvdXIgQ09WSUQtMTkgZGVtb2dyYXBoaWNzIGRhdGEuDQoNCmBgYHtyLCBmaWcud2lkdGg9MjAsIGZpZy5oZWlnaHQ9MjB9DQoNCmNvdmlkX2RlbW9ncmFwaGljc190b3RhbC5kZiAlPiUgDQogICMgVW5ncm91cCB0aGlzIGRhdGFmcmFtZSB0byBjbGVhbiBpdCB1cCBhIGxpdHRsZQ0KICB1bmdyb3VwKCkgJT4lIA0KICAjIEZpbHRlciBmb3IgY3VtdWxhdGl2ZSBkYXRhDQogIGZpbHRlcihwZXJpb2QgPT0gImN1bXVsYXRpdmUiKSAlPiUgDQogICMgU2VsZWN0IGZvciBqdXN0IHRoZSBpbXBvcnRhbnQgY29sdW1ucw0KICBzZWxlY3QocHVibGljX2hlYWx0aF91bml0LCBhZ2VfZ3JvdXAsIHBlcmNlbnRfY2FzZXMsIHBlcmNlbnRfaG9zcGl0YWxpemF0aW9ucykgJT4lIA0KICAjIFBpdm90IHRoZSBtb2RpZmllZCB0YWJsZSB0byBjYXB0dXJlIHRoZSAic3RhdF9ncm91cCIgb2YgcGVyY2VudF9jYXNlcyB2cyBwZXJjZW50X2hvc3BpdGFsaXphdGlvbnMNCiAgcGl2b3RfbG9uZ2VyKGNvbHM9YygzLDQpLCBuYW1lc190byA9ICJzdGF0X2dyb3VwIiwgdmFsdWVzX3RvID0gInBlcmNlbnRfUEhVX3RvdGFsIikgJT4lIA0KICANCiAgIyBDb21wYXJlIHRoZSBtZWFucyBvZiBvdXIgZ3JvdXBzIHdpdGhpbiB0aGUgZGF0YQ0KICBjb21wYXJlX21lYW5zKGZvcm11bGEgPSAuLi4sIA0KICAgICAgICAgICAgICAgIGRhdGEgPSAuLA0KICAgICAgICAgICAgICAgIGdyb3VwLmJ5ID0gLi4uLCANCiAgICAgICAgICAgICAgICBwLmFkanVzdC5tZXRob2QgPSAiaG9jaGJlcmciKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgNi40LjEgVXNlIGBnZW9tX3BjdygpYCB0byBhZGQgc2lnbmlmaWNhbmNlIGxldmVscyB0byB5b3VyIHBsb3RzDQoNCk5vdyB0aGF0IHdlJ3ZlIHNlZW4gaG93IGBjb21wYXJlX21lYW5zYCBnZW5lcmF0ZXMgb3V0cHV0LCB3ZSBjYW4gdXNlIHRoaXMga25vd2xlZGdlIHRvIGFkZCBwYWlyd2lzZSBjb21wYXJpc29uIHNpZ25pZmljYW5jZSBsZXZlbHMgKmRpcmVjdGx5KiB0byBvdXIgcGxvdHMgdXNpbmcgdGhlIGdncGxvdC1mcmllbmRseSBsYXllciBgZ2VvbV9wY3coKWAgd2hpY2ggd2lsbCBlc3NlbnRpYWxseSBhbm5vdGF0ZSBvdXIgcGxvdCB3aXRoIHRoZSBsZXZlbHMuDQoNClRoaXMgZnVuY3Rpb24gc2hhcmVzIG1hbnkgb2YgdGhlIHNhbWUgcGFyYW1ldGVycyBhcyBgY29tcGFyZV9tZWFucygpYCB3aXRoIGEgZmV3IGFkZGl0aW9uczoNCg0KLSAgIEl0IGRvZXMgbm90IHRha2UgYSBmb3JtdWxhIGJ1dCByYXRoZXIgZ2VuZXJhdGVzIG9uZSBiYXNlZCBvbiB5b3VyIGFlc3RoZXRpY3MgbWFwcGluZ3Mgb2YgYHhgLCBgeWAgYW5kIG90aGVyIGZhY3RvcnMuDQoNCi0gICBgbWFwcGluZ2A6IHRoZSBzYW1lIGtpbmQgb2YgbWFwcGluZyBwYXJhbWV0ZXJzIGFzIGFsbCBvdGhlciBnZW9tIGxheWVycywgdGhpcyBsZXQncyB1cyBzZXQgc29tZSBhZXN0aGV0aWNzIC0gbW9zdCBpbXBvcnRhbnRseSB0aGUgYGdyb3VwYCBhZXN0aGV0aWMuDQoNCi0gICBgeS5wb3NpdGlvbmA6IHRoZSB5LWF4aXMgdmFsdWUgYXQgd2hpY2ggd2Ugd2FudCB0byBkaXNwbGF5IG91ciBzaWduaWZpY2FuY2UgdmFsdWVzLiBUaGlzIGNhbiBiZSBhIHNpbmdsZSB2YWx1ZSBvciBhIHZlY3RvciBvZiB2YWx1ZXMgdG8gcmVwcmVzZW50IGVhY2ggY29tcGFyaXNvbi4NCg0KLSAgIGBtZXRob2RgOiBoZXJlIHRoZSBjaG9pY2Ugb2YgbWV0aG9kcyBkaWZmZXJzIGFuZCB0aGV5IGNvbWUgZnJvbSB0aGUgYHJzdGF0aXhgIHBhY2thZ2UgaW5jbHVkaW5nIGB3aWxjb3hfdGVzdGAsIGB0X3Rlc3RgLCBgZHVubl90ZXN0YCwgYW5kIGB0dWtleV9oc2RgDQoNCi0gICBgbWV0aG9kLmFyZ3NgOiBhIGxpc3Qgb2YgYWRkaXRpb25hbCBhcmd1bWVudHMgdGhhdCBhcmUgbmVlZGVkIGZvciB0aGUgdGVzdCBtZXRob2QuIEZvciBpbnN0YW5jZSBgdHVrZXlfaHNkYCB3aWxsIHJlcXVpcmUgYSBtb2RlbCBvYmplY3QgKGVnIGBsbWAgb3IgYGFvdmApIHRvIGRldGVybWluZSBpdHMgY29tcGFyaXNvbnMuDQoNCi0gICBgbGFiZWxgOiB0aGlzIGRldGVybWluZXMgdGhlIHNvdXJjZSBvZiB0aGUgbGFiZWxzIGZvciB5b3VyIHBsb3QuIFRoZXkgY2FuIGluY2x1ZGUgYHAuYWRqYCwgYHAuZm9ybWF0YCwgYW5kIGBwLnNpZ25pZmAgYXMgd2VsbCBhcyBhbiBleHByZXNzaW9uIHVzaW5nIHRoZSBzeW50YXggd2UgaGF2ZSBhbHJlYWR5IGxlYXJuZWQuDQoNClRoZXJlIGFyZSBtYW55IGFkZGl0aW9uYWwgcGFyYW1ldGVycyBnZW5lcmFsbHkgZm9yIHR3ZWFraW5nICpob3cqIHRoZSBkYXRhIGlzIGRpc3BsYXllZC4gWW91IGNhbiBmaW5kIGEgbGlzdCBvZiB0aGVzZSBvdmVyIG9uIHRoZSBbZ2dwdWJyIHJlZmVyZW5jZSBwYWdlXShodHRwczovL3Jwa2dzLmRhdGFub3ZpYS5jb20vZ2dwdWJyL3JlZmVyZW5jZS9nZW9tX3B3Yy5odG1sKQ0KDQpMZXQncyBhZGQgdGhlIFdpbGNveG9uIGNvbXBhcmlzb25zIGZyb20gb3VyIGFib3ZlIGFuYWx5c2lzIGRpcmVjdGx5IHRvIG91ciBncm91cGVkIHZpb2xpbiBwbG90cy4NCg0KYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD0xMn0NCg0KIyBCdWlsZCBhbmQgc2F2ZSB0aGUgcGxvdCBmb3IgbGF0ZXIgdXNlDQpkZW1vZ3JhcGhpY3MucGxvdCA8LSBjb3ZpZF9kZW1vZ3JhcGhpY3NfdG90YWwuZGYgJT4lIA0KICAjIFVuZ3JvdXAgdGhpcyBkYXRhZnJhbWUgdG8gY2xlYW4gaXQgdXAgYSBsaXR0bGUNCiAgdW5ncm91cCgpICU+JSANCiAgIyBGaWx0ZXIgZm9yIGN1bXVsYXRpdmUgZGF0YQ0KICBmaWx0ZXIocGVyaW9kID09ICJjdW11bGF0aXZlIikgJT4lIA0KICAjIFNlbGVjdCBmb3IganVzdCB0aGUgaW1wb3J0YW50IGNvbHVtbnMNCiAgc2VsZWN0KHB1YmxpY19oZWFsdGhfdW5pdCwgYWdlX2dyb3VwLCBwZXJjZW50X2Nhc2VzLCBwZXJjZW50X2hvc3BpdGFsaXphdGlvbnMpICU+JSANCiAgIyBQaXZvdCB0aGUgbW9kaWZpZWQgdGFibGUgdG8gY2FwdHVyZSB0aGUgInN0YXRfZ3JvdXAiIG9mIHBlcmNlbnRfY2FzZXMgdnMgcGVyY2VudF9ob3NwaXRhbGl6YXRpb25zDQogIHBpdm90X2xvbmdlcihjb2xzPWMoMyw0KSwgbmFtZXNfdG8gPSAic3RhdF9ncm91cCIsIHZhbHVlc190byA9ICJwZXJjZW50X1BIVV90b3RhbCIpICU+JSANCiAgIyBmaWx0ZXIoc3RhdF9ncm91cCA9PSAicGVyY2VudF9jYXNlcyIpICU+JSANCiAgDQogICMgUGxvdCB0aGUgZGF0YSBhcyBhIGdyb3VwZWQgdmlvbGluIHBsb3Qgd2l0aCBpbnNldCBib3hwbG90DQogIA0KICAjIDEuIERhdGENCiAgZ2dwbG90KC4pICsNCiAgICAjIDIuIEFlc3RoZXRpY3MNCiAgICBhZXMoeD1hZ2VfZ3JvdXAsIHkgPSBwZXJjZW50X1BIVV90b3RhbCkgKw0KDQogICAgIyBTdGFydCB3aXRoIGEgYmFzZSB0aGVtZQ0KICAgIHRoZW1lX21pbmltYWwoKSArDQoNCiAgICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MjApLCAjIHNldCB0ZXh0IHNpemUgdG8gMjANCiAgICAgICAgICANCiAgICAgICAgICAjIE1vdmUgdGhlIGxlZ2VuZCBhcm91bmQgdG8gd2l0aGluIHRoZSBwYW5lbCBzcGFjZQ0KICAgICAgICAgIGxlZ2VuZC5qdXN0aWZpY2F0aW9uID0gYygwLDEpLA0KICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC4wMiwwLjk1KSwNCiAgICAgICAgICBsZWdlbmQuZGlyZWN0aW9uID0gImhvcml6b250YWwiLCANCiAgICAgICAgICANCiAgICAgICAgICAjIFVwZGF0ZSB0aGUgcGFuZWwgdG8gZHJvcCB0aGUgbWlub3IgYXhpcyBncmlkIGxpbmVzDQogICAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICANCiAgICAgICAgICAjIFVzZSBhIGJsYWNrIGxpbmUgZm9yIHRoZSBheGVzDQogICAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIpLA0KICAgICAgICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChjb2xvdXIgPSAiYmxhY2siLCBmYWNlPSJib2xkIiksDQogICAgICAgICApICsNCiAgICANCiAgICAjIEFkZCBsYWJlbHMgdG8gdGhlIHBsb3QNCiAgICBsYWJzKHRpdGxlID0gIlBlcmNlbnQgY2FzZXMgYW5kIGhvc3BpdGFsaXphdGlvbnMgYnkgcHJvcG9ydGlvbiBwZXIgUEhVIGFjcm9zcyBhZ2UgZ3JvdXAiLA0KICAgICAgICAgeCA9ICJcbkFnZSBncm91cCIsDQogICAgICAgICB5ID0gIlByb3BvcnRpb24gb2YgcmVwb3J0ZWQgUEhVIGRhdGFcbiIsDQogICAgICAgICBjYXB0aW9uID0gIlxuKkFnZSBncm91cCB2YWx1ZXMgYXJlIGNhbGN1bGF0ZWQgYXMgYSBwZXJjZW50YWdlIG9mIHRvdGFsIGNhc2VzIG9yIGhvc3BpdGFsaXphdGlvbnMgd2l0aGluIGEgUEhVIikgKw0KDQogICAgIyBVc2UgdGhlIGd1aWRlcygpIGxheWVyIGFuZCBnZXQgcmlkIG9mIHRoZSBzY2FsZV9maWxsX2Rpc2NyZXRlKCkgbGF5ZXINCiAgICBndWlkZXMoZmlsbCA9ICJub25lIiwgZ3JvdXAgPSAibm9uZSIpICsNCg0KICAgICMgMy4gU2NhbGluZw0KICAgIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDAsIDAuNikpICsgICAgICAgICAjIFNldCB0aGUgbGltaXRzIG9mIG91ciB5LWF4aXMgDQoNCiAgICAjIFNldCB0aGUgbGFiZWxzIG9mIG91ciB4LWF4aXMgY2F0ZWdvcmllcw0KICAgIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzPWMoIjAtNCIsICI1LTExIiwgIjEyLTE5IiwgIjIwLTM5IiwgIjQwLTU5IiwgIjYwLTc5IiwgIjgwKyIpKSArDQoNCiAgICAjIFNldCB0aGUgY29sb3VyIGxlZ2VuZCANCiAgICBzY2FsZV9jb2xvdXJfZGlzY3JldGUobmFtZSA9ICJEYXRhIGNhdGVnb3J5IiwgbGFiZWxzID0gYygiJSBjYXNlcyIsICIlIGhvc3BpdGFsaXphdGlvbnMiKSkgKw0KDQogICAgIyA0LiBEYXRhDQogICAgIyBtdWx0aS1mYWN0b3IgdmlvbGluIHBsb3RzIGJ1dCBrZWVwIHRoZSB3aWR0aCBjb25zaXN0ZW50DQoNCiAgICAjIExpbmsgeW91ciBmaWxsIHRvIHRoZSBjb2xvdXIgYWVzdGhldGljDQogICAgZ2VvbV92aW9saW4oc2NhbGU9IndpZHRoIiwgDQogICAgICAgICAgICAgICAgYWVzKGNvbG91ciA9IHN0YXRfZ3JvdXAsIGZpbGw9YWZ0ZXJfc2NhbGUoYWxwaGEoY29sb3VyLCAwLjMpKSksIA0KICAgICAgICAgICAgICAgIGx3ZCA9IDEuNSkgKyANCg0KICAgICMgQm94cGxvdCBidXQgc21hbGxlciB3aWR0aCBzbyB0aGV5IHJlc2lkZSAid2l0aGluIiB0aGUgdmlvbGluIHBsb3QNCiAgICBnZW9tX2JveHBsb3QoYWVzKGZpbGwgPSBzdGF0X2dyb3VwKSwgd2lkdGg9MC4yLCANCiAgICAgICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aD0wLjkpLCANCiAgICAgICAgICAgICAgICAgb3V0bGllci5zaGFwZT1OQSkgKyAjIFJlbW92ZSB0aGUgb3V0bGllcnMNCg0KICAgICMgQWRkIGluIGFsbCBvZiB0aGUgZGF0YSBwb2ludHMNCiAgICBnZW9tX3F1YXNpcmFuZG9tKGRvZGdlLndpZHRoID0gMC44NSwgYWVzKGdyb3VwPXN0YXRfZ3JvdXApLCBhbHBoYSA9IDAuOCkgKw0KDQogICAgIyMjIDYuNC4xIEFkZCBpbiBzaWduaWZjYW5jZSB2YWx1ZXMgdG8geW91ciBwbG90DQogICAgIyBTZXQgdGhlIGdyb3VwaW5nIHRvIHVzZSBzdGF0X2dyb3VwIChsaWtlIGdyb3VwLmJ5KQ0KICAgIGdlb21fcHdjKG1hcHBpbmcgPSAuLi4sICAgIA0KICAgICAgICAgICAgICMgVXNlIGEgbm9uLXBhcmFtZXRyaWMgdGVzdA0KICAgICAgICAgICAgIG1ldGhvZCA9ICJ3aWxjb3hfdGVzdCIsICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgIyBMYWJlbCB3aXRoIHNpZ25pZmljYW5jZSBsZXZlbHMgaW5zdGVhZCBvZiBwLXZhbHVlcw0KICAgICAgICAgICAgIGxhYmVsID0gLi4uLCBsYWJlbC5zaXplID0gMTAsICANCiAgICAgICAgICAgICAjIFJlcG9zaXRpb24gdGhlIHktYXhpcyBsb2NhdGlvbiBvZiBpbmRpdmlkdWFsIGxhYmVscw0KICAgICAgICAgICAgIHkucG9zaXRpb24gPSBjKDAuMiwgMC4yLCAwLjIsIDAuNDUsIDAuNDUsIDAuNSwgMC41KSkgIA0KDQojIFNob3cgdGhlIHBsb3QNCmRlbW9ncmFwaGljcy5wbG90DQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCk5vdyB3ZSBjYW4gc2ltcGx5IHVwZGF0ZSBvdXIgZ2dhcnJhbmdlIHBsb3RzIQ0KDQpgYGB7ciwgZmlnLndpZHRoPTIwLCBmaWcuaGVpZ2h0PTIwfQ0KDQpwbG90IDwtDQoNCiMgQXJyYW5nZSB0aGUgdHdvIHBsb3RzIGluIGEgc2luZ2xlIHBhZ2UgICAgDQpnZ2FycmFuZ2UocGh1X2Nhc2VzLnBsb3QsIA0KICAgICAgICAgIGdnYXJyYW5nZShkZW1vZ3JhcGhpY3MucGxvdCArIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpLCAjIyMgNi4zLjAgcmVtb3ZlIHRoZSB0aXRsZQ0KICAgICAgICAgICAgICAgICAgICBwaHVfbWFyZ2luYWwucGxvdCwNCiAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiQiIsICJDIiksDQogICAgICAgICAgICAgICAgICAgIG5jb2wgPSAyLCANCiAgICAgICAgICAgICAgICAgICAgbnJvdyA9IDEsDQogICAgICAgICAgICAgICAgICAgIGZvbnQubGFiZWwgPSBsaXN0KHNpemU9MjApLCAjIG1ha2UgdGhlIGxhYmVscyBsYXJnZXINCiAgICAgICAgICAgICAgICAgICAgYWxpZ24gPSAiaCIgIyBUcnkgdG8gYWxpZ24gdGhlIHgtYXhpcyBvZiBib3RoIHBsb3RzDQogICAgICAgICAgICAgICAgICAgKSwNCiAgICAgICAgICBsYWJlbHMgPSBjKCJBIiksDQogICAgICAgICAgbmNvbCA9IDEsIA0KICAgICAgICAgIG5yb3cgPSAyLCANCiAgICAgICAgICBmb250LmxhYmVsID0gbGlzdChzaXplPTIwKSAjIE1hdGNoIHRoZSBpbmNyZWFzZWQgbGFiZWwgc2l6ZSBvZiB0aGUgb3RoZXIgcGxvdHMNCiAgICAgICAgICkNCnBsb3QgDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMgNy4wLjAgQ2xhc3Mgc3VtbWFyeQ0KDQpUb2RheSB3ZSBoYXZlICoqKmR1ZyBkZWVwKioqIGludG8gYWx0ZXJpbmcgYW5kIHBsYXlpbmcgd2l0aCBvdXIgcGxvdHMgdG8gaGVscCBnZXQgdGhlbSB0byB0aGF0IGV4dHJhIGxldmVsLiBBbHRob3VnaCB0aGVyZSBpcyBmYXIgbW9yZSB0byBleHBsb3JlLCB0aGlzIHNob3VsZCBjb3ZlciAqbW9zdCogb2YgeW91ciBuZWVkcyB3aGVuIGl0IGNvbWVzIHRvIGNsZWFuaW5nIHVwIHlvdXIgcGxvdHMuIFRvIHJlY2FwLCB3ZSd2ZSBsb29rZWQgYXQ6DQoNCjEuICBBbHRlcmluZyB0aGVtZXMgYW5kIGVsZW1lbnQgcG9zaXRpb25zLg0KMi4gIENvbnRyb2xsaW5nL3N1YnN0aXR1dGluZyB2YWx1ZXMgYW5kIGxhYmVscy4NCjMuICBDb2xvdXIgcGFsZXR0ZXMuDQo0LiAgQW5ub3RhdGluZyBwbG90cyB3aXRoIGFkZGl0aW9uYWwgZ2VvbXMuDQo1LiAgVGV4dC1iYXNlZCBmb3JtYXR0aW5nIGNoYW5nZXMuDQo2LiAgR2VuZXJhdGluZyBNdWx0aS1wbG90IGZpZ3VyZXMuDQo3LiAgQW5ub3RhdGluZyBwbG90cyB3aXRoIHN0YXRpc3RpY2FsIGFuYWx5c2VzLg0KDQpMb29raW5nIGEgbGl0dGxlIGJpdCBhaGVhZCBhdCB0aGlzIHdlZWsncyBhc3NpZ25tZW50LCB5b3Ugd2lsbCBsb29rIGF0IGNhbmFkYS13aWRlIHZhY2NpbmF0aW9uIGRhdGEuDQoNCllvdSBub3cgaGF2ZSB0aGUgdG9vbHMgdG8gY3JlYXRlIHBsb3RzIGxpa2UgdGhpczoNCg0KOjo6IHthbGlnbj0iY2VudGVyIn0NCjxpbWcgc3JjPSJodHRwczovL2dpdGh1Yi5jb20vY2Ftb2svQ1NCX0NvdXJzZV9NYXRlcmlhbHMvYmxvYi9tYWluL0FkdlZpei9Bc3NpZ25tZW50cy9BMy0xX3ZhY2NOdW1zLnBuZz9yYXc9dHJ1ZSIgd2lkdGg9IjEwMDAiLz4NCg0KT3ZlcmFsbCB2YWNjaW5hdGlvbiByYXRlcyBhbW9uZ3N0IHByb3ZpbmNlcyENCjo6Og0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgNy4xLjAgV2Vla2x5IGFzc2lnbm1lbnQNCg0KVGhpcyB3ZWVrJ3MgYXNzaWdubWVudCB3aWxsIGJlIGZvdW5kIHVuZGVyIHRoZSBjdXJyZW50IGxlY3R1cmUgZm9sZGVyIHVuZGVyIHRoZSAiYXNzaWdubWVudCIgc3ViZm9sZGVyLiBJdCB3aWxsIGluY2x1ZGUgYW4gUiBtYXJrZG93biBub3RlYm9vayB0aGF0IHlvdSB3aWxsIHVzZSB0byBwcm9kdWNlIHRoZSBjb2RlIGFuZCBhbnN3ZXJzIGZvciB0aGlzIHdlZWsncyBhc3NpZ25tZW50LiBQbGVhc2UgcHJvdmlkZSBhbnN3ZXJzIGluIG1hcmtkb3duIG9yIGNvZGUgY2VsbHMgdGhhdCBpbW1lZGlhdGVseSBmb2xsb3cgZWFjaCBxdWVzdGlvbiBzZWN0aW9uLg0KDQp8ICAgICAgICAgICAgICAgICAgICB8IEFzc2lnbm1lbnQgYnJlYWtkb3duIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfDotLS0tLS0tLS0tLS0tLS0tLTp8Oi0tLS0tLS0tLS0tLS0tLS0tOnw6LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfA0KfCAgICAgICAgQ29kZSAgICAgICAgfCAgICAgICAgIDUwJSAgICAgICAgICB8IFwtIERvZXMgaXQgZm9sbG93IGJlc3QgcHJhY3RpY2VzPyAgICAgICAgICAgICAgIHwNCnwgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgfCBcLSBEb2VzIGl0IG1ha2UgZ29vZCB1c2Ugb2YgYXZhaWxhYmxlIHBhY2thZ2VzPyB8DQp8ICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgIHwgXC0gV2FzIGRhdGEgcHJlcGFyZWQgcHJvcGVybHkgICAgICAgICAgICAgICAgICAgfA0KfCBBbnN3ZXJzIGFuZCBPdXRwdXQgfCAgICAgICAgIDUwJSAgICAgICAgICB8IFwtIElzIG91dHB1dCBiYXNlZCBvbiB0aGUgY29ycmVjdCBkYXRhc2V0PyAgICAgIHwNCnwgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgfCBcLSBBcmUgZ3JvdXBpbmdzIGFwcHJvcHJpYXRlICAgICAgICAgICAgICAgICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgIHwgXC0gQXJlIGNvcnJlY3QgdGl0bGVzL2F4ZXMvbGVnZW5kcyBjb3JyZWN0PyAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICB8IFwtIElzIGludGVycHJldGF0aW9uIG9mIHRoZSBncmFwaHMgY29ycmVjdD8gICAgIHwNCg0KU2luY2UgY29kaW5nIHN0eWxlcyBhbmQgc29sdXRpb25zIGNhbiBkaWZmZXIsIHN0dWRlbnRzIGFyZSBlbmNvdXJhZ2VkIHRvIHVzZSBiZXN0IHByYWN0aWNlcy4gQXNzaWdubWVudHMgKm1heSogYmUgcmV3YXJkZWQgZm9yIHdlbGwtY29kZWQgb3IgZWxlZ2FudCBzb2x1dGlvbnMuDQoNCllvdSBjYW4gc2F2ZSBhbmQgZG93bmxvYWQgdGhlIG1hcmtkb3duIG5vdGVib29rIGluIGl0cyBuYXRpdmUgZm9ybWF0LiBTdWJtaXQgdGhpcyBmaWxlIHRvIHRoZSB0aGUgYXBwcm9wcmlhdGUgYXNzaWdubWVudCBzZWN0aW9uIGJ5IDEyOjU5IHBtIG9uIHRoZSBkYXRlIG9mIG91ciBuZXh0IGNsYXNzOiBBcHJpbCA0dGgsIDIwMjQuDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyA3LjIuMCBBY2tub3dsZWRnZW1lbnRzDQoNCioqUmV2aXNpb24gMS4wLjAqKjogY3JlYXRlZCBhbmQgcHJlcGFyZWQgZm9yICoqQ1NCMTAyMUggUyBMRUMwMTQxKiosIDAzLTIwMjEgYnkgQ2FsdmluIE1vaywgUGguRC4gKkJpb2luZm9ybWF0aWNpYW4sIEVkdWNhdGlvbiBhbmQgT3V0cmVhY2gsIENBR0VGLioNCg0KKipSZXZpc2lvbiAxLjAuMSoqOiBlZGl0ZWQgYW5kIHByZXBhcmVkIGZvciAqKkNTQjEwMjBIIFMgTEVDMDE0MSoqLCAwMy0yMDIyIGJ5IENhbHZpbiBNb2ssIFBoLkQuICpCaW9pbmZvcm1hdGljaWFuLCBFZHVjYXRpb24gYW5kIE91dHJlYWNoLCBDQUdFRi4qDQoNCioqUmV2aXNpb24gMS4wLjIqKjogZWRpdGVkIGFuZCBwcmVwYXJlZCBmb3IgKipDU0IxMDIwSCBTIExFQzAxNDEqKiwgMDMtMjAyMyBieSBDYWx2aW4gTW9rLCBQaC5ELiAqQmlvaW5mb3JtYXRpY2lhbiwgRWR1Y2F0aW9uIGFuZCBPdXRyZWFjaCwgQ0FHRUYuKg0KDQoqKlJldmlzaW9uIDIuMC4wKio6IFJldmlzZWQgYW5kIHByZXBhcmVkIGZvciAqKkNTQjEwMjBIIFMgTEVDMDE0MSoqLCAwMy0yMDI0IGJ5IENhbHZpbiBNb2ssIFBoLkQuICpCaW9pbmZvcm1hdGljaWFuLCBFZHVjYXRpb24gYW5kIE91dHJlYWNoLCBDQUdFRi4qDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyA3LjMuMCBSZWZlcmVuY2VzDQoNClRoZSBSIEdyYXBoIEdhbGxlcnk6IDxodHRwczovL3d3dy5yLWdyYXBoLWdhbGxlcnkuY29tL2luZGV4Lmh0bWw+DQoNCkRpZmZlcmVudCBhZXN0aGV0aWNzIHBhcmFtZXRlcnMgaW4gYGdncGxvdCgpYDogPGh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9hZXNfZ3JvdXBfb3JkZXIuaHRtbD4NCg0KV2hpY2ggYWVzdGhldGljcyBjYW4gYmUgYWx0ZXJlZCBmb3IgZGlmZmVyZW50IGdlb21zPzogPGh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9hZXNfbGluZXR5cGVfc2l6ZV9zaGFwZS5odG1sPg0KDQpBZHZhbmNlZCBleGFtcGxlcyBvZiBkaXJlY3QgbGFiZWxpbmcgd2l0aCBgZ2VvbV9kbCgpYDogPGh0dHBzOi8vZGlyZWN0bGFiZWxzLnItZm9yZ2Uuci1wcm9qZWN0Lm9yZy9leGFtcGxlcy5odG1sPg0KDQpNb3JlIGluZm9ybWF0aW9uIGFib3V0IHRoZSBgZ2doaWdobGlnaHRgIHBhY2thZ2U6IDxodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvZ2doaWdobGlnaHQvdmlnbmV0dGVzL2dnaGlnaGxpZ2h0Lmh0bWw+DQoNClVzaW5nIGBleHByZXNzaW9uKClgOiA8aHR0cHM6Ly9zdGF0LmV0aHouY2gvUi1tYW51YWwvUi1kZXZlbC9saWJyYXJ5L2dyRGV2aWNlcy9odG1sL3Bsb3RtYXRoLmh0bWw+DQoNClVzaW5nIGBicXVvdGUoKWA6IDxodHRwczovL3d3dy5yLWJsb2dnZXJzLmNvbS8yMDE4LzAzL21hdGgtbm90YXRpb24tZm9yLXItcGxvdC10aXRsZXMtZXhwcmVzc2lvbi1hbmQtYnF1b3RlLz4NCg0KTW9yZSBvcHRpb25zIGZvciBgZ2dhcnJhbmdlKClgOiA8aHR0cHM6Ly9ycGtncy5kYXRhbm92aWEuY29tL2dncHVici9yZWZlcmVuY2UvZ2dhcnJhbmdlLmh0bWw+DQoNCkxlYXJuaW5nIHNvbWUgb2YgdGhlIGZ1bmN0aW9ucyBmb3IgYGdnRXh0cmFgOiA8aHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL2dnRXh0cmEvdmlnbmV0dGVzL2dnRXh0cmEuaHRtbD4NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIFRoZSBDZW50ZXIgZm9yIHRoZSBBbmFseXNpcyBvZiBHZW5vbWUgRXZvbHV0aW9uIGFuZCBGdW5jdGlvbiAoQ0FHRUYpDQoNClRoZSBDZW50cmUgZm9yIHRoZSBBbmFseXNpcyBvZiBHZW5vbWUgRXZvbHV0aW9uIGFuZCBGdW5jdGlvbiAoQ0FHRUYpIGF0IHRoZSBVbml2ZXJzaXR5IG9mIFRvcm9udG8gb2ZmZXJzIGNvbXByZWhlbnNpdmUgZXhwZXJpbWVudGFsIGRlc2lnbiwgcmVzZWFyY2gsIGFuZCBhbmFseXNpcyBzZXJ2aWNlcyBpbiBtaWNyb2Jpb21lIGFuZCBtZXRhZ2Vub21pYyBzdHVkaWVzLCBnZW5vbWljcywgcHJvdGVvbWljcywgYW5kIGJpb2luZm9ybWF0aWNzLg0KDQpGcm9tIHRhcmdldGVkIEROQSBhbXBsaWNvbiBzZXF1ZW5jaW5nIHRvIHRyYW5zY3JpcHRvbWVzLCB3aG9sZSBnZW5vbWVzLCBhbmQgbWV0YWdlbm9tZXMsIGZyb20gcHJvdGVpbiBpZGVudGlmaWNhdGlvbiB0byBwb3N0LXRyYW5zbGF0aW9uYWwgbW9kaWZpY2F0aW9uLCBDQUdFRiBoYXMgdGhlIHRvb2xzIGFuZCBrbm93bGVkZ2UgdG8gc3VwcG9ydCB5b3VyIHJlc2VhcmNoLiBPdXIgc3RhdGUtb2YtdGhlLWFydCBmYWNpbGl0eSBhbmQgZXhwZXJpZW5jZWQgcmVzZWFyY2ggc3RhZmYgcHJvdmlkZSBhIGJyb2FkIHJhbmdlIG9mIHNlcnZpY2VzLCBpbmNsdWRpbmcgYm90aCBzdGFuZGFyZCBhbmFseXNlcyBhbmQgdGVjaG5pcXVlcyBkZXZlbG9wZWQgYnkgb3VyIHRlYW0uIEluIHBhcnRpY3VsYXIsIHdlIGhhdmUgc3BlY2lhbCBleHBlcnRpc2UgaW4gbWljcm9iaWFsLCBwbGFudCwgYW5kIGVudmlyb25tZW50YWwgc3lzdGVtcy4NCg0KRm9yIG1vcmUgaW5mb3JtYXRpb24gYWJvdXQgdXMgYW5kIHRoZSBzZXJ2aWNlcyB3ZSBvZmZlciwgcGxlYXNlIHZpc2l0IDxodHRwczovL3d3dy5jYWdlZi51dG9yb250by5jYS8+Lg0KDQo6Ojoge2FsaWduPSJjZW50ZXIifQ0KPGltZyBzcmM9Imh0dHBzOi8vZ2l0aHViLmNvbS9jYW1vay9DU0JfQ291cnNlX01hdGVyaWFscy9ibG9iL21haW4vQWR2Vml6L0NBR0VGX25ldy5wbmc/cmF3PXRydWUiIHdpZHRoPSI3MDAiLz4NCjo6Og0K